java

Criar uma Loja Virtual Grátis
java

 

Manual Java – Aprenda programação Java para iniciantes

 

Você pode usar Java para construir servidores, criar aplicativos desktop, jogos, aplicativos mobile e muito mais. Existem também outras linguagens JVM (discutiremos o que isso significa em breve), como  Kotlin  ,  Groovy  ,  Scala  e  Clojure,  que você pode usar para diferentes propósitos.

Java também é multiplataforma, o que significa que o código que você escreve e compila em uma plataforma pode ser executado em qualquer outra plataforma que tenha Java instalado. Discutiremos esse tópico com mais detalhes posteriormente.

Por enquanto, posso dizer que, embora o Java tenha sua cota de falhas, ele também tem muito a oferecer.

Índice

  • Pré-requisitos
  • Como escrever Hello World em Java
    • O que está acontecendo no código?
    • O que é JVM?
    • O que é JRE e JDK?
  • Como configurar o Java no seu computador?
  • Como instalar um IDE Java no seu computador?
  • Como criar um novo projeto no IntelliJ IDEA
  • Como trabalhar com variáveis em Java
    • Quais são as regras para declarar variáveis?
    • O que são variáveis finais?
  • Quais são os tipos de dados primitivos em Java?
    • O que é conversão ou conversão de tipo?
    • O que são classes wrapper em Java
  • Como usar operadores em Java
    • O que são operadores aritméticos?
    • O que são os operadores de atribuição?
    • O que são operadores relacionais?
    • O que são operadores lógicos?
    • O que são operadores unitários?
  • Como trabalhar com strings em Java
    • Como formatar uma string
    • Como obter o comprimento de uma string ou verificar se ela está vazia ou não
    • Como dividir e unir cordas
    • Como converter uma string para autoridades ou minúsculas
    • Como comparar duas strings
    • Como substituir caracteres ou substrings em uma string
    • Como verificar se uma string contém uma substring ou não
  • Quais são as diferentes maneiras de inserir e enviar dados?
  • Como usar instruções condicionais em Java
  • O que é uma instrução switch-case?
  • Qual é o escopo de variável em Java?
  • Quais são os valores padrões das variáveis em Java?
  • Como trabalhar com matrizes em Java
    • Como classificar uma matriz
    • Como realizar uma pesquisa binária em uma matriz
    • Como preencher uma matriz
    • Como fazer cópia de um array
    • Como comparar dois arrays
  • Como usar loops em Java
    • Para Laço
    • Loop para cada
    • Enquanto Laço
    • Laço Do-While
  • Como trabalhar com listas de array em Java
    • Como adicionar ou remover vários elementos
    • Como remover elementos com base em uma condição
    • Como clonar e comparar listas de array
    • Como verificar se um elemento está presente ou se a lista de matrizes está vazia
    • Como classificar uma lista de matriz
    • Como manter elementos comuns de duas listas de array
    • Como executar uma ação em todos os elementos de uma lista de matriz
  • Como trabalhar com mapas de hash em Java
    • Como colocar ou substituir vários elementos em um mapa de hash
    • Como verificar se um mapa de hash contém um item ou se está vazio
    • Como executar uma ação em todos os elementos de um mapa de hash
  • Classes e Objetos em Java
    • O que é um método?
    • O que é sobrecarga de método?
  • O que são construtores em Java?
  • O que são modificadores de acesso em Java?
  • O que são os métodos Getter e Setter em Java?
  • O que é herança em Java?
  • Como substituir um método em Java
  • Conclusão

Pré-requisitos

O único requisito para este curso é familiaridade com qualquer outra linguagem de programação, como Python, JavaScript e assim por diante.

Embora eu explique conceitos cruciais de programação no contexto de Java, não expliquei coisas como o que é uma variável no contexto da programação em geral.

Como escrever Hello World em Java

O ideal seria que o primeiro passo fosse configurar o Java no seu computador, mas não quero te entender como baixar e instalar um monte de software logo de cara. Para este exemplo, você usará  https://replit.com/  como plataforma.

Primeiro, acesse  https://replit.com/  e crie uma nova conta, caso ainda não tenha uma. Você pode usar sua conta atual do Google/GitHub/Facebook para fazer login. Após o login, você será direcionado para sua página inicial. Lá, use o botão  Criar  em  Meus Reps  para criar um novo representante.

Imagem

No modal  Criar um Repl  , escolha  Java  como  Modelo, defina um  Título  descritivo como  HelloWorld  e clique no botão  Criar Repl  .

Imagem

Um editor de código será exibido com um terminal integrado da seguinte maneira:

Imagem

No lado esquerdo está a lista de arquivos neste projeto, no meio está o editor de código e no lado direito está o terminal.

O modelo vem com algum código por padrão. Você pode repeti-lo clicando no botão  Executar  . Vá em frente e execute o programa.

Imagem

Se tudo correr bem, você verá as palavras "Olá, mundo!" impressas no lado direito. Parabéns, você executou seu primeiro programa Java com sucesso.

O que está acontecendo no código?

O programa hello world é provavelmente o programa Java mais básico que você pode escrever – e entender esse programa é crucial.

class Main {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

Vamos começar com a primeira linha:

class Main {
  //...
}

Esta linha cria uma  Mainclasse. Uma classe agrupa um conjunto de códigos relacionados em uma única unidade.

Esta é uma  publicclasse, o que significa que pode ser acessada em qualquer lugar da base de código. Um arquivo-fonte Java (arquivos com extensão .js) pode conter apenas uma classe  .javade nível superior .public

Esta classe pública de nível superior deve ter o mesmo nome do arquivo de código-fonte. É por isso que o arquivo nomeado  Main.javacontém a  mainclasse neste projeto.

Para entender o porquê, clique nos três pontos na lista de arquivos e clique na opção  Mostrar arquivos ocultos  .

Imagem

Isso revelará alguns novos arquivos dentro do projeto. Entre eles está o  Main.classarquivo. Ele é chamado de bytecode. Ao clicar no botão Executar, o compilador Java compilou o código do  Main.javaarquivo para este bytecode.

Agora, modifique o código Hello World existente da seguinte forma:

class Main {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

class NotMain {
  public static void main(String[] args) {
    System.out.println("Not hello world!");
  }
}

Como você pode ver, uma nova classe chamada  NotMainfoi adicionada. Vá em frente e clique no botão  Executar  mais uma vez, mantendo os olhos no menu  Arquivos  .

Imagem

Um novo bytecode chamado  NotMain.classapareceu. Isso significa que, para cada classe que você tiver em toda a sua base de código, o compilador criará um bytecode separado.

Isso cria confusão sobre qual classe é o ponto de entrada para este programa. Para resolver esse problema, o Java usa uma classe que corresponde ao nome do arquivo de código-fonte como ponto de entrada para este programa.

Chega de falar da aula, agora vamos dar uma olhada na função dentro dela:

class Main {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

A  public static void main (String[] args)função é especial em Java. Se você tem experiência com linguagens como C, C++ ou Go, já deve saber que todo programa nessas linguagens tem uma função principal. A execução do programa começa a partir dessa função principal.

Em Java, você precisa escrever essa função exatamente como está,  public static void main (String[] args)caso contrário ela não funcionará. Aliás, se você alterar, mesmo que seja só um minuto, o Java começa a gritar.

Imagem

O tipo de retorno mudou de  voidpara  inta função agora retorna  0no final. Como você pode ver no console, ele diz:

Error: Main method must return a value of type void in class Main, please 
define the main method as:
   public static void main(String[] args)

Ouça essa sugestão e reverta seu programa para como ele era antes.

class Main {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

O  mainmétodo é um  publicmétodo e,  staticportanto, você pode chamá-lo sem instanciar sua classe.

Isso  voidsignifica que a função não retorna nenhum valor e que  String[] argsa função recebe um array de strings como argumento. Esse array contém argumentos de linha de comando passados ao programa durante uma execução.

Imprime  System.out.printlnstrings sem terminal. No exemplo acima, "  "Hello world!"foi passado para a função", então você recebe  Hello world!uma string impressa no terminal.

Em Java, toda instrução termina com um ponto e vírgula.  Ao contrário de JavaScript ou Python, o ponto e vírgula em Java é obrigatório  . Deixar um de fora causará falha na construção.

É basicamente isso para este programa. Se você não entendeu cada aspecto desta seção palavra por palavra, não se preocupe. As coisas ficarão muito mais claras à medida que você avança.

Por enquanto, lembre-se de que o nível superior  public class em um arquivo de origem Java deve corresponder ao nome do arquivo, e a função principal de qualquer programa Java deve ser definida como  public static void main(String[] args).

O que é JVM?

Já mencionei a palavra "bytecode" algumas vezes na seção anterior. Também disse que Java é "multiplataforma", o que significa que o código escrito e compilado em uma plataforma pode ser executado em qualquer plataforma que tenha Java instalado.

Veja bem, seu processador não entende inglês. Na verdade, a única coisa que ele entende são zeros e uns, também conhecidos como binários.

Quando você escreve e compila um programa em C++, ele resulta em um arquivo binário. Seu processador o entende e, dependendo da plataforma de destino do programa, esse arquivo pode ser diferente.

Tomemos como exemplo um processador AMD64 e um ARMv8-A. Esses processadores têm conjuntos de instruções diferentes. Portanto, para executar seu programa nessas duas plataformas diferentes, você terá que compilá-los separadamente.

Mas um programa Java pode ser escrito uma vez e executado em qualquer lugar. Espero que você se lembre dos bytecodes que mencionamos na seção anterior. Quando você compila código Java, ele não resulta em binário, mas sim em bytecode.

Este bytecode não é totalmente binário, mas também não é legível por humanos. Aliás, seu processador também não consegue lê-lo.

Então, em vez de enviar esse bytecode para a CPU, nós o executamos na Máquina Virtual Java (JVM). A JVM então lê e interpreta o bytecode para a CPU.

Se você quiser entender a arquitetura da JVM em um nível mais profundo, sugiro  o artigo detalhado de  Siben Nayak  sobre o assunto.

O que é JRE e JDK?

JRE significa Java Runtime Environment e JDK significa Java Development Kit.

O JRE ou Java Runtime Environment reúne uma implementação da JVM junto com um conjunto de bibliotecas necessárias para executar programas Java.

O JDK, por outro lado, empacota o JRE junto com todas as bibliotecas necessárias para desenvolver programas Java.

Então, se você quiser executar programas Java no seu computador, instale o JRE. Se quiser desenvolver programas Java você mesmo, instale o JDK. Existem várias implementações do JDK.

Existe o  Java SE (Standard Edition) Development Kit  da Oracle e o  OpenJDK , uma implementação de referência oficial do Java SE (Standard Edition) Development Kit.

Como você pode perceber pelo nome do OpenJDK, ele é de código aberto. Portanto, existem várias versões dele. Se você estiver em uma máquina Linux e usar o gerenciador de pacotes de sua distribuição para instalar o JDK, é bem provável que você instale uma versão do OpenJDK, como  Adoptium  ,  Microsoft Build do OpenJDK  e assim por diante.

Espero que você entenda que o JRE é um superconjunto da JVM e o JDK é um superconjunto do JRE. Não se preocupe com implementações ou compilações diferentes no momento, você terá quando chegar a hora.

Como configurar o Java no seu computador

Primeiro, acesse  https://www.oracle.com/java/technologies/downloads/  e baixe a versão mais recente do  Java SE Development Kit  de acordo com a plataforma em que você está:

Imagem

Após a conclusão do download, inicie o instalador e siga o processo de instalação clicando no botão Avançar. Conclua clicando no botão Fechar na última página.

Imagem

O processo de instalação pode variar no macOS e no Linux, mas você deve descobrir sozinho.

Após a conclusão do comando de instalação, execute o seguinte em seu terminal:

java --version

# java 18.0.2 2022-07-19
# Java(TM) SE Runtime Environment (build 18.0.2+9-61)
# Java HotSpot(TM) 64-Bit Server VM (build 18.0.2+9-61, mixed mode, sharing)

Para funcionar, você instalou o Java SE Development Kit em seu computador com sucesso. Se preferir usar o OpenJDK, sinta-se à vontade para baixar  a versão da Microsoft do OpenJDK  ou  o Adoptium  e siga o processo de instalação.

Para os programas de exemplo simples que escreveremos neste artigo, não importa qual JDK você está usando. Mas, na prática, comprovou-se que sua versão do JDK funciona perfeitamente com o tipo de projeto em que você está trabalhando.

Como instalar um IDE Java no seu computador

Quando se trata de Java,  o IntelliJ IDEA  é inegavelmente o melhor IDE disponível. Até o Google o utiliza como base para o  Android Studio  .

A versão final do IDE pode  custar até US$ 149,00 por ano para um indivíduo  . Mas se você for estudante, pode obter  licenças educacionais  para todos os produtos JetBrains gratuitamente.

Há também uma edição comunitária, totalmente gratuita e de código aberto. É a que usaremos ao longo de todo o livro.

Acesse a  página de download do IntelliJ IDEA  e baixe a edição da comunidade para sua plataforma.

Imagem

Quando o download terminar, use o instalador para instalar o IntelliJ IDEA como qualquer outro software.

Como criar um novo projeto no IntelliJ IDEA

Use o atalho do menu Iniciar para iniciar o IntelliJ IDEA. A seguinte janela será exibida:

Imagem

Use o botão  Novo Projeto e uma janela  Novo Projeto  será exibida:

Imagem

Dê um nome descritivo ao seu projeto. Deixe as demais opções como estão e clique no botão  Criar  .

A criação do projeto não deve levar mais do que alguns instantes e, quando estiver concluída, a seguinte janela será exibida:

Imagem

Esta é a janela de ferramentas do projeto no lado esquerdo. Todo o seu código-fonte ficará dentro dessa  src pasta.

Clique com o botão direito do mouse na  src pasta e vá em  Novo > Classe Java .

Imagem

Na próxima etapa, coloque um nome, como  Main para sua classe, e certifique-se de  que Classe  esteja destacado como o tipo.

Imagem

Uma nova classe será criada com algumas linhas de código.

Imagem

Atualize o código da seguinte maneira:

public class Main {
    public static void main (String[] args) {
        System.out.println("Hello World!");
    }
}

Para executar este código, use o botão verde de reprodução no lado direito da barra superior.

Imagem

O código será executado e a saída será exibida no terminal integrado na parte inferior da janela.

Imagem

Parabéns, você recriou com sucesso o  HelloWorld programa discutido anteriormente no IntelliJ IDEA.

Como trabalhar com variáveis em Java

Para trabalhar com diferentes tipos de dados em Java, você pode criar variáveis de diferentes tipos. Por exemplo, se quiser armazenar sua idade em uma nova variável, faça assim:

public class Main {

    public static void main(String[] args) {
        //  
        int age;

    }

}

Comece escrevendo o tipo de dado ou variável. Como  age é um número inteiro, seu tipo será inteiro ou,  int para abreviar, seguido pelo nome da variável  age e um ponto e vírgula.

No momento, você declarou a variável, mas não a inicializou. Em outras palavras, a variável não possui valor. Você pode inicializá-la da seguinte maneira:

public class Main {

    public static void main(String[] args) {
        //  
        int age;

        //  = 
        age = 27;

        // prints the age on the terminal
        System.out.println("I am " + age + " years old.");

    }

}

Ao atribuir um valor, comece escrevendo o nome da variável que deseja inicializar, seguido por um sinal de igual (chamado operador de atribuição) e, em seguida, o valor que deseja atribuir à variável. E não se esqueça do ponto e vírgula no final.

A  System.out.println(); chamada de função imprimirá a linha  I am 27 years old. no console. Caso você esteja se perguntando, usar um sinal de mais é uma das muitas maneiras de imprimir variáveis dinamicamente no meio de uma frase.

Uma coisa que você precisa ter em mente é que não é possível usar uma variável não inicializada em Java. Portanto, se você comentar a linha  age = 27 colocando duas barras antes dela e tentar compilar o código, o compilador exibirá a seguinte mensagem de erro:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    The local variable age may not have been initialized

    at variables.Main.main(Main.java:13)

A linha  The local variable age may not have been initialized indica que a variável não foi inicializada.

Em vez de declarar e inicializar a variável em linhas diferentes, você pode fazer isso de uma só vez, da seguinte maneira:

public class Main {

    public static void main(String[] args) {
        //   = 
        int age = 27;

        // prints the age on the terminal
        System.out.println("I am " + age + " years old.");

    }

}

O código deve voltar ao normal. Além disso, você pode alterar o valor de uma variável quantas vezes quiser no seu código.

public class Main {

    public static void main(String[] args) {
        int age = 27;

        // updates the value to be 28 instead of 27
        age = 28;

        System.out.println("I am " + age + " years old.");

    }

}

Neste código, o valor de  age mudará de 27 para 28 porque você o está substituindo antes da impressão.

Tenha em mente que, embora você possa atribuir valores a uma variável quantas vezes quiser, não é possível declarar a mesma variável duas vezes.

public class Main {

    public static void main(String[] args) {
        //   = 
        int age = 27;

        int age = 28;

        // prints the age on the terminal
        System.out.println("I am " + age + " years old.");

    }

}

Se você tentar compilar este código, o compilador exibirá a seguinte mensagem de erro:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Duplicate local variable age

    at variables.Main.main(Main.java:9)

A linha  Duplicate local variable ageindica que a variável já foi declarada.

Além de variáveis, você pode encontrar o termo "literal" na internet. Literais são variáveis com valores fixos.

Por exemplo, aqui,  age = 27e não é calculado dinamicamente. Você escreveu o valor diretamente no código-fonte. Assim  agecomo um literal inteiro.

Quais são as regras para declarar variáveis?

Existem algumas regras quando se trata de nomear variáveis em Java. Você pode nomeá-los com qualquer nome, desde que não comece com um número e não possa conter espaços.

Embora seja possível iniciar um nome de variável com um sublinhado (_) ou um cifrão ($), não se atentar ao uso deles pode dificultar a leitura do código. Nomes de variáveis também diferenciam fronteiras de mineiras. Portanto,  agee  AGEsão duas variáveis diferentes.

Outra coisa importante a lembrar é que você não pode usar nenhuma das palavras-chave reservadas pelo Java. Existem cerca de 50 delas atualmente. Você pode aprender sobre essas palavras-chave na  documentação oficial,  mas não se preocupe em memorizá-las.

À medida que você pratica, informações importantes serão infiltradas em seus neurônios automaticamente. E se você ainda errar na declaração de uma variável, o compilador estará lá para lembrá-lo de que algo está errado.

Além das regras, existem algumas convenções que você deve seguir:

  • Comece o nome da sua variável com uma letra minúscula e nenhum caractere especial (como um sublinhado ou um cifrão).
  • Se o nome da variável tiver várias palavras, use camel case:  firstName,lastName
  • Não use nomes com uma única letra:  f,l

Contanto que você siga essas regras e convenções, está tudo certo. Se quiser saber mais sobre convenções de nomenclatura em geral,  confira meu artigo sobre o assunto  .

O que são  finalvariáveis?

Uma  finalvariável em Java só pode ser inicializada uma vez. Portanto, se você declarar uma variável como  final, não poderá reatribuí-la.

public class Main {

    public static void main(String[] args) {
        // final   = 
        final int age = 27;

        age = 28;

        System.out.println("I am " + age + " years old.");

    }

}

Como a  agevariável foi declarada como  final, o código exibirá a seguinte mensagem de erro:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    The final local variable age cannot be assigned. It must be blank and not using a compound assignment

    at variables.Main.main(Main.java:9)

Entretanto, se você deixar uma variável não inicializada durante a declaração, o código funcionará:

public class Main {

    public static void main(String[] args) {
        // final  
        final int age;

        age = 28;

        // prints the age on the terminal
        System.out.println("I am " + age + " years old.");

    }

}

Portanto, declare uma variável como  final________ limitará sua capacidade de reatribuir seu valor. Se você deixá-la sem inicializar, poderá inicializá-la normalmente.

Quais são os tipos de dados primitivos em Java?

Em um nível mais alto, existem dois tipos de dados em Java: os "tipos primitivos" e os "tipos não primitivos" ou "tipos de referência".

Tipos primitivos armazenam valores. Por exemplo,  inté um tipo primitivo e armazena um valor inteiro.

Um tipo de referência, por outro lado, armazena a referência em um local de memória onde um objeto sonoro está sendo armazenado.

Existem oito tipos de dados primitivos em Java.

TIPO EXPLICAÇÃO
byte Inteiro assinado de 8 bits no intervalo de -128 a 127
short Inteiro assinado de 16 bits no intervalo de -32.768 a 32.767
int Inteiro assinado de 32 bits no intervalo de -2147483648 a 2147483647
long Inteiro assinado de 64 bits no intervalo de -9223372036854775808 a 9223372036854775807
float ponto flutuante de 32 bits de precisão simples dentro do intervalo de 1,4E-45 a 3,4028235E38
double ponto flutuante de 64 bits de precisão dupla dentro do intervalo de 4,9E-324 a 1,7976931348623157E308
boolean Pode ser qualquer um  true ou false
char caractere Unicode único de 16 bits no intervalo de  u0000 (ou 0) a  uffff (ou 65.535 inclusive)

Sim, sim, eu sei que a tabela parece assustadora, mas não se estresse. Você não precisa decorá-los.

Você não precisará pensar sobre esses intervalos com muita frequência e, mesmo que precise, há maneiras de imprimi-los no seu código Java.

Entretanto, se você não entende o que é um bit, eu recomendaria  este pequeno artigo  para aprender sobre binário.

Você já aprendeu sobre como declarar um inteiro na seção anterior. Você pode declarar um  byte,  short, e  long da mesma forma.

Declarar a  double também funciona da mesma maneira, exceto que você pode atribuir um número com um ponto decimal em vez de um inteiro:

public class Main {

    public static void main(String[] args) {
        double gpa = 4.8;

        System.out.println("My GPA is " + gpa + ".");

    }
}

Se você atribuir um  int ao  double, como  4 em vez de  4.8, a saída será  4.0 em vez de  4, porque  double sempre terá um ponto decimal.

Como  double e  float são semelhantes, você pode pensar que substituir a  double palavra-chave por  float converterá essa variável para um número de ponto flutuante – mas isso não é correto. Você terá que adicionar um  f ou  F após o valor:

public class Main {

    public static void main(String[] args) {
        float gpa = 4.8f;

        System.out.println("My GPA is " + gpa + ".");

    }
}

Isso acontece porque, por padrão, todo número com vírgula decimal é tratado como um  double em Java. Se você não anexar o  f, o compilador pensará que você está tentando atribuir um  double valor a uma  float variável.

boolean os dados podem conter  valores true ou  false .

public class Main {

    public static void main(String[] args) {
        boolean isWeekend = false;

        System.out.println(isWeekend); // false

    }
}

Como você pode imaginar,  false pode ser tratado como um não e  true pode ser tratado como um sim.

Os booleanos se tornarão muito mais úteis depois que você aprender sobre instruções condicionais. Então, por enquanto, lembre-se apenas do que elas são e o que podem conter.

O  char tipo pode conter qualquer caractere Unicode dentro de um determinado intervalo.

public class Main {

    public static void main(String[] args) {
        char percentSign = '%';

        System.out.println(percentSign); // %

    }
}

Neste exemplo, você salvou o sinal de porcentagem dentro de uma  char variável e o imprimiu no terminal.

Você também pode usar sequências de escape Unicode para imprimir determinados símbolos.

public class Main {

    public static void main(String[] args) {
        char copyrightSymbol = 'u00A9';

        System.out.println(copyrightSymbol); // ©

    }
}

A sequência de escape Unicode para o símbolo de direitos autorais, por exemplo, é  u00A9 e você pode encontrar mais sequências de escape Unicode  neste site .

Dentre esses 8 tipos de dados, você trabalhará com  int,  double,  booleane  char na maioria das vezes.

O que é conversão ou conversão de tipo?

A conversão de tipos em Java pode ser "implícita" ou "explícita". Quando o compilador converte automaticamente um tipo menor de dado em um maior, isso é conhecido como conversão implícita ou de restrição.

public class Main {

    public static void main(String[] args) {
        int number1 = 8;
        double number2 = number1;

        System.out.println(number2); // 8.0
    }

}

Como um double é maior que um inteiro, o compilador poderia facilmente realizar a conversão. Se você tentar fazer o inverso, no entanto, encontrará o seguinte erro do compilador:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Type mismatch: cannot convert from double to int

    at operators.Main.main(Main.java:7)

Ao executar uma conversão implícita, o fluxo de conversão deve ser o seguinte:

Imagem

Você pode, é claro, ir de um  short para um  double, por exemplo, pulando os outros no meio.

Você também pode migrar de tipos de dados menores para maiores. Isso é chamado de conversão de tipo explícita ou de ampliação.

package datatypes;

public class Main {

    public static void main(String[] args) {
        double number1 = 8.5;
        int number2 = (int) number1;

        System.out.println(number2); // 8
    }

}

Você já viu que, ao tentar converter um tipo de dado maior em um menor, o compilador reclama. Mas, ao adicionar o  (int) operador de conversão explicitamente, você mostra ao compilador quem manda.

Ao fazer isso, você perde parte dos seus dados. Se alterar o  double número inicial de  8.5 para apenas  8.0, você não perderá nenhuma informação. Portanto, sempre que realizar uma conversão explícita, tenha cuidado.

Você também pode converter a  char para an  int da seguinte maneira:

public class Main {

    public static void main(String[] args) {
        char character = 'F';
        int number = character;

        System.out.println(number); // 70
    }

}

70 é o código ASCII do caractere  F – é por isso que a saída ficou assim. Se você quiser saber mais sobre códigos ASCII, meu colega  Kris Koishigawa  escreveu  um excelente artigo  sobre o assunto.

O fluxo de conversão neste caso será o oposto do que você já viu.

Imagem

Sugiro que você experimente converter vários valores de um tipo para outro e veja o que acontece. Isso aprofundará sua compreensão e o deixará mais confiante.

O que são classes wrapper em Java?

Classes wrapper podem envolver tipos de dados primitivos e transformá-los em tipos de referência. Classes wrapper estão disponíveis para todos os oito tipos de dados primitivos.

Tipo primitivo Classe Wrapper
int Integer
long Long
short Short
byte Byte
boolean Boolean
char Character
float Float
double Double

Você pode usar essas classes wrapper da seguinte maneira:

public class Main {
    public static void main (String[] args) {
        Integer age = 27;
        Double gpa = 4.8;

        System.out.println(age); // 27
        System.out.println(gpa); // 4.8
    }
}

Tudo o que você precisa fazer é substituir o tipo de dado primitivo pela classe wrapper equivalente. Esses tipos de referência também possuem métodos para extrair o tipo primitivo deles.

Por exemplo,  age.intValue() retornará a idade como um inteiro primitivo e  gpa.doubleValue() retornará o GPA em um tipo double primitivo.

Existem métodos semelhantes para todos os oito tipos de dados. Embora você use os tipos primitivos na maioria das vezes, essas classes wrapper serão úteis em alguns cenários que discutiremos em uma seção posterior.

Como usar operadores em Java

Operadores em programação são certos símbolos que dizem ao compilador para executar certas operações, como operações aritméticas, relacionais ou lógicas.

Embora existam seis tipos de operadores em Java, não falarei sobre operadores bit a bit aqui. Discutir operadores bit a bit em um guia para iniciantes pode ser intimidador.

O que são operadores aritméticos?

Operadores aritméticos são aqueles que você pode usar para realizar operações aritméticas. Existem cinco deles:

OPERADOR OPERAÇÃO
+ Adição
- Subtração
* Multiplicação
/ Divisão
% Resto (Módulo/Módulo)

As operações de adição, subtração, multiplicação e divisão são bastante autoexplicativas. Veja o exemplo de código a seguir para entender:

public class Main {

    public static void main(String[] args) {
        int number1 = 10;
        int number2 = 5;

        System.out.println(number1 + number2); // 15
        System.out.println(number1 - number2); // 5
        System.out.println(number1 * number2); // 50
        System.out.println(number1 / number2); // 2
        System.out.println(number1 % number2); // 0

    }

}

As saídas das quatro primeiras operações não precisam de explicação. Na última operação, você executou uma operação módulo/módulo usando o  % símbolo. O resultado é  0 que, se você dividir 10 por 2, não sobrará nada (sem resto).

As operações de adição e multiplicação são bastante simples. Mas, ao realizar uma subtração, se o primeiro operando for maior que o segundo, o resultado será um número negativo, assim como na vida real.

O tipo de dados com o qual você está trabalhando faz diferença no resultado das operações de divisão e módulo.

public class Main {

    public static void main(String[] args) {
        int number1 = 8;
        int number2 = 5;

        System.out.println(number1 / number2); // 1
    }

}

Embora o resultado dessa operação devesse ser 1,6, isso não aconteceu porque, em Java, se você dividir um inteiro por outro inteiro, o resultado será um inteiro. Mas se você transformar ambos ou um deles em um float/double, tudo voltará ao normal.

public class Main {

    public static void main(String[] args) {
        double number1 = 8;
        double number2 = 5;

        System.out.println(number1 / number2); // 1.6
    }

}

Este princípio também se aplica às operações de módulo. Se ambos ou um dos operandos for um float/double, o resultado será um float/double.

O que são os operadores de atribuição?

Você já trabalhou com o operador de atribuição em uma seção anterior.

public class Main {

    public static void main(String[] args) {
        //   = 
        int age = 27;

        // prints the age on the terminal
        System.out.println(age);

    }

}

Quando você usa o  = símbolo para atribuir um valor a uma variável, ele funciona como um operador de atribuição. Mas esta não é a única forma deste operador.

Combinando o operador de atribuição regular com os operadores aritméticos, você pode obter resultados diferentes.

OPERADOR OPERAÇÃO EQUIVALENTE A
+= a += b a = a + b
-= a -= b a = a - b
*= a *= b a = a * b
/= a /= b a = a / b
%= a %= b a = a % b

O exemplo de código a seguir deve deixar as coisas mais claras:

package operators;

public class Main {

    public static void main(String[] args) {
        double number1 = 10;
        double number2 = 5;

        number1 += number2;

        System.out.println(number1); // 15
    }

}

Os outros operadores funcionam da mesma forma. Eles operam e atribuem o valor resultante ao operando da esquerda.

Eu poderia demonstrar os outros usando código, mas acho que se você mesmo os experimentar, entenderá melhor. Afinal, experimentação e prática são as únicas maneiras de consolidar seu conhecimento.

O que são operadores relacionais?

Operadores relacionais são usados para verificar a relação entre operandos, como, por exemplo, se um operando é igual a outro operando ou não.

Esses operadores relacionais retornam  true ou ,  false dependendo da operação que você realizou.

Existem seis operadores relacionais em Java.

OPERADOR EXPLICAÇÃO USO
== É igual a 5 == 8 retornos false
!= Não é igual a 5 != 8 retornos true
> É maior que 5 > 8 retornos false
< É menor que 5 < 8 retornos true
>= Maior ou igual a 5 >= 8 retornos false
<= Menor ou igual a 5 <= 8 retornos true

O exemplo de código a seguir demonstra o uso desses operadores:

public class Main {

    public static void main(String[] args) {
        double number1 = 10;
        double number2 = 5;

        System.out.println(number1 == number2); // false
        System.out.println(number1 != number2); // true
        System.out.println(number1 > number2); // true
        System.out.println(number1 < number2); // false
        System.out.println(number1 >= number2); // true
        System.out.println(number1 <= number2); // false
    }

}

O uso prático desses operadores ficará muito mais claro para você depois que aprender sobre instruções condicionais em uma seção posterior.

Você também pode usar esses operadores com caracteres.

public class Main {

    public static void main(String[] args) {
        char smallLetter = 'a';
        char capitalLetter = 'A';

        System.out.println(smallLetter > capitalLetter); // ???
    }

}

Qual você acha que será a saída deste código? Descubra você mesmo. Lembra dos valores ASCII dos caracteres? Eles desempenham um papel na saída deste programa.

O que são operadores lógicos?

Imagine um cenário em que um programa que você criou só pode ser usado por pessoas com 18 anos ou mais, mas não maiores de 40 anos. Portanto, a lógica seria a seguinte:

can run the program if ->
    age >= 18 and age <= 40

Ou, em outro cenário, um usuário precisa ser aluno da sua escola ou membro da biblioteca para pegar livros emprestados. Nesse caso, a lógica seria a seguinte:

can borrow books if ->
    isSchoolStudent or isLibraryMember

Essas decisões lógicas podem ser tomadas usando operadores lógicos. Existem três desses operadores em Java.

OPERADOR USO EXPLICAÇÃO
Lógico E ( &&) age >= 18 && age <= 40 Avalia como verdadeiro somente se ambas as condições forem verdadeiras
Lógico Ou (`   `) `éEstudanteEscolar   éMembro da Biblioteca` Avalia como verdadeiro se uma das duas ou ambas as condições forem verdadeiras
Não ( !) !isLibraryMember Avalia como falso se a condição interna for avaliada como verdadeira e vice-versa

Vamos ver esses operadores em código. Primeiro, o  and operador lógico:

public class Main {

    public static void main(String[] args) {
        int age = 20;

        System.out.println(age >= 18 && age <= 40); // true
    }

}

Neste caso, há duas condições em cada lado do  && operador. Se, e somente se, ambas as condições forem avaliadas como  true, a  and operação será avaliada como  true.

Se a primeira condição for avaliada como  false, o computador não avaliará as demais condições e retornará  false. Porque se a primeira condição for avaliada como  false, não há como toda a operação ser avaliada como  true.

O operador lógico  or funciona de forma semelhante, mas neste caso, se qualquer uma das condições for verdadeira, toda a operação será avaliada como verdadeira:

public class Main {

    public static void main(String[] args) {
        boolean isSchoolStudent = true;
        boolean isLibraryMember = false;

        System.out.println(isSchoolStudent || isLibraryMember); // true
    }

}

or Se a primeira condição de uma operação  lógica  for avaliada como true, o computador não avaliará as demais condições e retornará  true. Porque se a primeira condição for avaliada como ,  true a operação será avaliada como ,  true independentemente do resultado das outras condições.

Por fim, o  not operador é avaliado como o oposto do valor da sua condição. Veja o seguinte exemplo de código:

public class Main {

    public static void main(String[] args) {
        boolean isLibraryMember = true;

        System.out.println(isLibraryMember); // true
        System.out.println(!isLibraryMember); // false
    }

}

Como você pode ver, o operador not retorna o oposto do  boolean valor fornecido. O operador not é um operador unário, o que significa que opera em um único operando.

public class Main {

    public static void main(String[] args) {
        boolean isLibraryMember = true;
        boolean isSchoolStudent = false;

        System.out.println(!isSchoolStudent || isLibraryMember); // true
    }

}

Neste exemplo, o operador not se transforma  isSchoolStudent em  true, então a operação é avaliada como  true. No entanto, se você modificar o código da seguinte forma:

public class Main {

    public static void main(String[] args) {
        boolean isLibraryMember = true;
        boolean isSchoolStudent = false;

        System.out.println(!(isSchoolStudent || isLibraryMember)); // false
    }

}

Primeiro, a operação lógica "or" será executada e avaliada como  true. O operador "not" a transformará em  false.

Embora você tenha usado dois operandos com cada operador, pode usar quantos quiser. Você também pode misturar e combinar vários operadores.

public class Main {

    public static void main(String[] args) {
        boolean isSchoolStudent = true;
        boolean isLibraryMember = false;
        int age = 10;

        System.out.println(isSchoolStudent || isLibraryMember && age > 18); // ???
    }

}

Qual você acha que será o resultado deste código? Recomendo que você descubra por si mesmo. :)

O que são operadores unários?

Existem alguns operadores que são usados com um operando por vez e são chamados de operadores unários. Embora existam cinco deles, discutirei apenas dois.

OPERADOR EXPLICAÇÃO
Incremento ( ++) Incrementa um valor fornecido em 1
Decremento ( --) Decrementa um valor dado em 1

O exemplo de código a seguir irá demonstrá-los bem:

public class Main {

    public static void main(String[] args) {
        int score = 95;
        int turns = 11;

        score++;
        turns--;

        System.out.println(score); // 96
        System.out.println(turns); // 10
    }

}

Você também pode usar os operadores como prefixos:

public class Main {

    public static void main(String[] args) {
        int score = 95;
        int turns = 11;

        ++score;
        --turns;

        System.out.println(score); // 96
        System.out.println(turns); // 10
    }

}

Até aqui, tudo é simples. Mas existem algumas pequenas diferenças entre as sintaxes pós-fixada e prefixada que você precisa entender. Observe o código a seguir:

package operators;

public class Main {

    public static void main(String[] args) {
        int score = 95;


        System.out.println(++score); // 96
        System.out.println(score); // 96
    }

}

Este é o comportamento esperado. O operador de decremento de prefixo funcionará da mesma forma. Mas veja o que acontece se você mudar para a versão pós-fixada:

package operators;

public class Main {

    public static void main(String[] args) {
        int score = 95;


        System.out.println(score++); // 95
        System.out.println(score); // 96
    }

}

Confuso, não é? Qual você acha que é o valor real da variável agora? É 96. Deixe-me explicar.

Ao usar a sintaxe pós-fixada em uma função print, a função print encontra a variável primeiro e depois a incrementa. É por isso que a segunda linha imprime o valor recém-atualizado.

No caso da sintaxe de prefixo, a função encontra primeiro o operador de incremento e executa a operação. Em seguida, passa a imprimir o valor atualizado.

Essa pequena diferença pode te pegar desprevenido se você não tomar cuidado. Ou se tentar evitar incrementos ou decrementos em chamadas de função.

Como trabalhar com strings em Java

O  String tipo em Java é um dos tipos de referência mais usados. É uma coleção de caracteres que você pode usar para formar linhas de texto no seu programa.

Existem duas maneiras de criar novas strings em Java. A primeira é a maneira literal:

public class Main {
    public static void main(String[] args) {
        String name = "Farhan";

        System.out.println("My name is " + name + ".");
    }

}

Como você pode ver, declarar e usar  String this dessa maneira não é muito diferente de declarar os tipos primitivos em Java.

A segunda maneira de criar um novo  String é usando o  new operador.

public class Main {
    public static void main(String[] args) {
        //   = new ()
        String name = new String("Farhan");

        System.out.println("My name is " + name + ".");
    }

}

Este programa funcionará exatamente como o anterior, mas há uma pequena diferença entre os dois.

A JVM mantém uma parte da memória do seu computador para armazenar strings. Essa parte é chamada de pool de strings.

Sempre que você cria um novo objeto  String literalmente, a JVM primeiro verifica se ele  String já existe no pool. Se existir, a JVM o reutilizará. Caso contrário, a JVM o criará.

Por outro lado, ao usar o  new operador, a JVM sempre criará um novo  String objeto, não importa o que aconteça. O programa a seguir demonstra esse conceito claramente:

public class Main {

    public static void main(String[] args) {
        String literalString1 = "abc";
        String literalString2 = "abc";

        String objectString1 = new String("abc");
        String objectString2 = new String("abc");

        System.out.println(literalString1 == literalString2);
        System.out.println(objectString1 == objectString2);

    }

}

Como você já deve saber, o  == operador é usado para verificar a igualdade. A saída deste programa será:

true
false

Como  abc já estava no conjunto de strings, a  literalString2 variável o reutiliza. No caso das strings de objeto, no entanto, ambas são entidades diferentes.

Como formatar uma string

Você já viu o uso do  + operador para costurar strings ou formatá-las de uma maneira específica.

Essa abordagem funciona até que você tenha muitas adições a uma string. É fácil errar o posicionamento das aspas.

Uma maneira melhor de formatar uma string é o  String.format() método .

public class Main {
    public static void main(String[] args) {
        String name = "Farhan";
        int age = 27;

        String formattedString = String.format("My name is %s and I'm %d years old.", name, age);

        System.out.println(formattedString);
    }

}

O método recebe uma string com especificadores de formato como seu primeiro argumento e argumentos para substituir esses especificadores como argumentos posteriores.

No código acima, os caracteres  %ssão  %despecificados de formato. Eles são responsáveis por informar ao compilador que esta parte da string será recuperada por algo.

Em seguida, o compilador substituirá o  %spelo  namee  %do pelo  age. A ordem dos especificadores precisa corresponder à ordem dos argumentos, e os argumentos precisam corresponder ao tipo do especificador.

Os especificados  %se  %dnão são aleatórios. São específicos para dados de string e números inteiros decimais. Segue uma tabela com os especificados comumente usados:

Especificador Tipo de dados
%b,%B Booleano
%s,%S Corda
%c,%C Caractere Unicode
%d Decimal Inteiro
%f Números de pontos flutuantes

Há também  %opara números inteiros octais,  %xou  %Xpara números hexadecimais, e/  %eou  %Epara notações científicas. Mas, como não os discutiremos neste livro, deixei-os de fora.

Assim como os especificadores  %se  %do que você viu, você pode usar qualquer um desses especificadores para o tipo de dado correspondente. E, caso você esteja perguntando, esse  %fespecificador funciona tanto para floats quanto para doubles.

Como obter o comprimento de uma string ou verificar se ela está vazia ou não

Verifique o comprimento de uma string ou certifique-se de que ela não está vazia antes de executar alguma operação que seja uma tarefa comum.

Cada objeto string vem com um  length()método que retorna o comprimento dessa string. É como uma  lengthpropriedade para arrays.

public class Main {
    public static void main(String[] args) {
        String name = "Farhan";

        System.out.println(String.format("Length of this string is: %d.", name.length())); // 6
    }

}

O método retorna o comprimento como um inteiro. Portanto, você pode usá-lo livremente em conjunto com o especificador de formato inteiro.

Para verificar se uma string está vazia ou não, você pode usar o  isEmpty()método . Assim como o  length()método, ele também vem com todos os objetos string.

public class Main {
    public static void main(String[] args) {
        String name = "Farhan";

        if (name.isEmpty()) {
            System.out.println("There is no name mentioned here");
        } else {
            System.out.println(String.format("Okay, I'll take care of %s.", name));
        }
    }

}

O método retorna um valor booleano para que você possa usá-lo diretamente nas instruções if. O programa verifica se o nome está vazio ou não e imprime respostas diferentes com base nisso.

Como dividir e unir cordas

O  split()método pode dividir uma string com base em uma expressão regular.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String name = "Farhan Hasin Chowdhury";

        System.out.println(Arrays.toString(name.split(" ")));
    }

}

O método retorna um array de strings. Cada string nesse array será uma substring da string original. Aqui, por exemplo, você está quebrando uma string  Farhan Hasin Chowdhuryem cada espaço. Portanto, a saída será  [Farhan, Hasin, Chowdhury].

Apenas um lembrete de que matrizes são coleções de vários dados do mesmo tipo.

Como o método recebe uma regex como argumento, você pode usar expressões regulares para executar operações de divisão mais complexas.

Você também pode unir esse array novamente em uma string como esta:

public class Main {
    public static void main(String[] args) {
        String name = "Farhan Hasin Chowdhury";

        String substrings[] = name.split(" ");

        String joinedName = String.join(" ", substrings);

        System.out.println(joinedName); // Farhan Hasin Chowdhury
    }

}

O  join()método também pode ajudá-lo a unir várias strings fora de um array.

public class Main {
    public static void main(String[] args) {
        System.out.println(String.join(" ", "Farhan", "Hasin", "Chowdhury")); // Farhan Hasin Chowdhury
    }

}

Como converter uma string para autoridades ou minúsculas

Converter uma string para hierarquias ou minúsculas é muito simples em Java. Existem métodos  toUpperCase()e  toLowerCase()métodos especificamente indicados para executar as tarefas:

public class Main {
    public static void main(String[] args) {
        String name = "Farhan Hasin Chowdhury";

        System.out.println(name.toUpperCase()); // FARHAN HASIN CHOWDHURY

        System.out.println(name.toLowerCase()); // farhan hasin chowdhury
    }

}

Como comparar duas strings

Como strings são tipos de referência, você não pode compará-las usando o  =operador.

O  equals()método verifica se duas strings são iguais ou não e  equalsIgnoreCase()ignora a diferenciação de características e minúsculas ao comparar.

public class Main {
    public static void main(String[] args) {
        String name = "Farhan Hasin Chowdhury";
        String nameUpperCase = name.toUpperCase();

        System.out.println(name.equals(nameUpperCase)); // false

        System.out.println(name.equalsIgnoreCase(nameUpperCase)); // true
    }

}

Como substituir caracteres ou substrings em uma string

O  replace()método pode substituir caracteres ou substrings inteiros de uma determinada string.

package strings;

public class Main {
    public static void main(String[] args) {
        String loremIpsumStd = "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.";

        System.out.println(String.format("Standard lorem ipsum text: %s", loremIpsumStd));

        String loremIpsumHalfTranslated = loremIpsumStd.replace("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium", "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system");

        System.out.println(String.format("Translated lorem ipsum text: %s", loremIpsumHalfTranslated));
    }

}

Aqui, a  loremIpsumStdstring contém uma parte do texto original de Lorem Ipsum. Em seguida, você substitui a primeira linha dessa string e salva a nova string na  loremIpsumHalfTranslatedvariável.

Como verificar se uma string contém uma substring ou não

O  contains()método pode verificar se uma determinada string contém uma determinada substring ou não.

public class Main {
    public static void main(String[] args) {
        String lyric = "Roses are red, violets are blue";

        if (lyric.contains("blue")) {
            System.out.println("The lyric has the word blue in it.");
        } else {
            System.out.println("The lyric doesn't have the word blue in it.");
        }
    }

}

O método retorna um valor booleano, então você pode usar a função em qualquer instrução condicional.

Existem alguns dos métodos de string mais comuns. Se quiser saber mais sobre os outros, sinta-se à vontade para consultar  a documentação oficial  .

Quais são as diferentes maneiras de inserir e enviar dados?

Até agora, você aprendeu sobre o  System.out.println()método para imprimir informações no terminal. Você também aprendeu sobre o  String.format()método em uma seção anterior.

Nesta seção, você aprenderá sobre alguns irmãos do  System.out.println() método. Você também aprenderá sobre como receber informações do usuário.

Receber informações do usuário é extremamente fácil em linguagens como Python. No entanto, em Java, são necessárias algumas linhas de código a mais.

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("What's your name? ");
        String name = scanner.nextLine();

        System.out.printf("So %s. How old are you? ", name);
        int age = scanner.nextInt();

        System.out.printf("Cool! %d is a good age to start programming.", age);

        scanner.close();

    }

}

A  java.util.Scanner classe é necessária para receber entradas do usuário. Você pode trazer a classe para o seu programa usando a  import palavra-chave .

Em seguida, você precisará criar uma nova instância da  Scanner classe usando a  new palavra-chave . Ao criar a nova instância, você deverá informar o fluxo de entrada desejado.

Você pode querer receber a entrada do usuário ou de um arquivo. Seja qual for, você terá que informar o compilador. O  System.in fluxo é o fluxo padrão de entrada e saída.

O objeto scanner tem métodos como  nextLine() para receber entrada de string,  nextInt() para receber entrada de inteiro,  nextDouble() para receber entrada dupla e assim por diante.

No código acima, o  scanner.nextLine() método solicitará uma string do usuário e retornará a entrada fornecida com um caractere de nova linha anexado.

O  scanner.nextInt() método então solicitará um inteiro e retornará o número fornecido pelo usuário.

Talvez você esteja vendo o  System.out.printf() método pela primeira vez aqui. Bem, além do  System.out.println() método, há também o  System.out.print() método que imprime uma string específica sem adicionar um caractere de quebra de linha a ela.

É  System.out.printf() uma espécie de combinação dos  métodos System.out.print() e  String.format() . Você também pode usar os especificadores de formato discutidos anteriormente neste método.

Após terminar de receber a entrada, você precisará fechar o objeto scanner. Para isso, basta chamar o  scanner.close() método .

Simples, não é? Vou complicar um pouco.

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("What's your name? ");
        String name = scanner.nextLine();

        System.out.printf("So %s. How old are you? ", name);
        int age = scanner.nextInt();

        System.out.printf("Cool! %d is a good age to start programming. 
What language would you prefer? ", age);
        String language = scanner.nextLine();

        System.out.printf("Ah! %s is a solid programming language.", language);

        scanner.close();

    }

}

Adicionei uma nova  scanner.nextLine() instrução após a  scanner.nextInt() chamada do método. Vai funcionar?

Não, não vai. O programa simplesmente pulará o último prompt de entrada e imprimirá a última linha. Esse comportamento não é exclusivo de  scanner.nextInt(). Se você usar  scanner.nextLine() depois de qualquer um dos outros  nextWhatever() métodos, enfrentará esse problema.

Resumindo, isso acontece porque quando você pressiona enter no  scanner.nextInt() método, ele consome o inteiro e deixa o caractere de nova linha no buffer de entrada.

Portanto, quando  scanner.nextLine() é invocado, ele consome esse caractere de nova linha como o fim da entrada. A solução mais fácil para esse problema é escrever uma  scanner.nextLine() chamada adicional após as outras chamadas do método do scanner.

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("What's your name? ");
        String name = scanner.nextLine();

        System.out.printf("So %s. How old are you? ", name);
        int age = scanner.nextInt();

        // consumes the dangling newline character
        scanner.nextLine();

        System.out.printf("Cool! %d is a good age to start programming. 
What language would you prefer? ", age);
        String language = scanner.nextLine();

        System.out.printf("Ah! %s is a solid programming language.", language);

        scanner.close();

    }

}

Existe outra maneira de resolver este problema. Mas não vou entrar em detalhes aqui. Se tiver interesse,  confira meu artigo sobre o assunto .

Como usar instruções condicionais em Java

Use instruções condicionais para tomar decisões com base em condições.

Isso é feito usando a  if instrução a seguir:

public class Main {

    public static void main(String[] args) {
        int age = 20;

        // if (condition) {...}
        if (age >= 18 && age <= 40) {
            System.out.println("you can use the program");
        }

    }

}

A instrução começa com um  if e, em seguida, há a condição dentro de um par de parênteses. Se a condição for avaliada como verdadeira, o código entre chaves será executado.

O código entre chaves é conhecido como bloco de código.

Se você alterar o valor de  age para ,  50 a instrução print não será executada e não haverá saída no console. Para esses tipos de situações em que a condição é avaliada como  false, você pode adicionar um  else bloco:

public class Main {

    public static void main(String[] args) {
        int age = 20;

        if (age >= 18 && age <= 40) {
            System.out.println("you can use the program");
        } else {
            System.out.println("you can not use the program");
        }

    }

}

Agora, se a condição for avaliada como  false, o código dentro do  else bloco será executado e você verá  you can not use the program impresso no seu terminal.

Você também pode ter várias condições dentro de uma  if-else if-else escada:

public class Main {

    public static void main(String[] args) {
        int age = 50;
        boolean isSchoolStudent = true;
        boolean isLibraryMember = false;

        // if (condition) {...}
        if (age >= 18 && age <= 40) {
            System.out.println("you can use the program");
        } else if (isSchoolStudent || isLibraryMember) {
            System.out.println("you can use the program for a short time");
        } else {
            System.out.println("you can not use the program");
        }

    }

}

Agora, se a primeira condição for avaliada como  false , a segunda condição será testada. Se a segunda condição for avaliada como ,  true o código entre chaves será executado. Se as condições em ambas  if as instruções forem avaliadas como  false, o  else bloco será executado.

Você também pode aninhar  if instruções dentro de outras  if instruções da seguinte maneira:

package operators;

public class Main {

    public static void main(String[] args) {
        int age = 20;

        if (age >= 18 && age <= 40) {
            boolean isSchoolStudent = true;
            boolean isLibraryMember = false;

            if (isSchoolStudent || isLibraryMember) {
                System.out.println("you can use the program");
            }
        } else {
            System.out.println("you can not use the program");
        }

    }

}

Nesse caso,  if a declaração interna será testada somente se a primeira declaração for avaliada como verdadeira  if .

O que é uma instrução switch-case?

Além dos blocos if-else, também existem casos de switch onde você pode definir vários casos com base em um único switch.

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("What is the first operand? ");
        int a =scanner.nextInt();

        // consumes the dangling newline character
        scanner.nextLine();

        System.out.print("What is the second operand? ");
        int b = scanner.nextInt();

        // consumes the dangling newline character
        scanner.nextLine();

        System.out.print("What operation would you like to perform? ");
        String operation = scanner.nextLine();

        switch (operation) {
            case "sum":
                System.out.printf("%d + %d = %d", a, b, a+b);
                break;
            case "sub":
                System.out.printf("%d - %d = %d", a, b, a-b);
                break;
            case "mul":
                System.out.printf("%d * %d = %d", a, b, a*b);
                break;
            case "div":
                if (b == 0) {
                    System.out.print("Can't divide by zero!");
                } else {
                    System.out.printf("%d / %d = %d", a, b, a / b);
                }
                break;
            default:
                System.out.printf("Invalid Operation!");
        }

        scanner.close();

    }

}

Este é um programa de calculadora muito simples. O programa solicita ao usuário dois números e, em seguida, pergunta qual operação ele gostaria de realizar.

Cada instrução switch-case terá um switch e vários cases. Quando você diz  case "sum", o programa verifica se o valor do switch ou da  operation variável em this é válido  sum ou não.

Se houver correspondência, o corpo do caso será executado. Se nenhum dos casos corresponder, o  default caso será executado.

E sobre essa  break afirmação. Ela faz o que parece: impede o programa de avançar para o próximo caso.

Se você remover as  break instruções, todos os casos serão executados um após o outro até que o  default caso seja alcançado.

O que é escopo de variável em Java?

Escopo é o tempo de vida e a acessibilidade de uma variável. Dependendo de onde você declara uma variável, você pode ou não conseguir acessá-la de outros lugares.

Veja o seguinte trecho de código como exemplo:

public class Main {

    public static void main(String[] args) {
        int age = 20;

        if (age >= 18 && age <= 40) {
            // age variable is accessible here
            // booleans are not accessible here

            boolean isSchoolStudent = true;
            boolean isLibraryMember = false;

            if (isSchoolStudent || isLibraryMember) {
                // booleans are accessible here
                // age variable is accessible here

                System.out.println("you can use the program");
            }
        } else {
            // age variable is accessible here
            // booleans are not accessible here

            System.out.println("you can not use the program");
        }

    }

}

Aqui, a  age variável é declarada dentro do  class bloco de código. Isso significa que você pode acessá-la dentro de toda a classe sem problemas. Como a variável é acessível em toda a instância da classe, ela é uma variável de instância.

No entanto, as  variáveis isSchoolStudent e  isLibraryMember foram declaradas dentro do primeiro  if bloco de código de instrução. Portanto, elas não serão acessíveis fora desse bloco de código.

Mas ele estará acessível em qualquer bloco de código aninhado dentro do primeiro  if bloco. Essas são chamadas de variáveis locais.

Há também variáveis de classe declaradas usando a  static palavra-chave, mas você aprenderá sobre elas nas seções de programação orientada a objetos.

Então, por enquanto, a regra geral é que uma variável estará acessível dentro do bloco de código em que foi declarada e qualquer outro bloco de código aninhado dentro do bloco pai.

Quais são os valores padrões das variáveis em Java?

Você aprendeu que em Java é preciso inicializar uma variável depois de declará-la. Caso contrário, você não conseguirá usar isso. Bem, isso não é verdade em todos os casos.

Se você declarar uma variável no nível de classe, essa variável receberá um valor padrão do compilador.

public class Main {

    // gets 0 as the value by default
    static int age;

    public static void main(String[] args) {
        System.out.println(age); // 0
    }
}

Como o  main método é  static, ele só pode acessar  static variáveis no nível de classe. Discutirei isso  static com mais detalhes na seção de programação orientada a objetos.

Mas se você mover a declaração da variável dentro do  main método, ela se torna local para esse método e não recebe nenhum valor padrão.

public class Main {
    public static void main(String[] args) {
        // remains uninitialized
        int age;

        System.out.println(age); // fails to compile
    }
}

Este código gerará o  The local variable age may not have been initialized erro na compilação.

As variáveis recebem seus valores padrão com base em seu tipo. Na maioria dos casos, será  0 ou  null. Estou listando todos os tipos primitivos e seus valores padrão:

Tipo de dados Valor padrão
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char 'u0000'
boolean false

Qualquer tipo de referência receberá o valor  null por padrão. Discutiremos tipos de referência, classes e objetos nas seções de programação orientada a objetos.

Como trabalhar com matrizes em Java

Você já aprendeu a declarar variáveis únicas e usá-las no seu programa. É aqui que entram os arrays.

Matrizes são estruturas de dados que contêm múltiplos valores do mesmo tipo de dado em memórias sequenciais. Matrizes podem ser de qualquer tipo de dado primitivo ou não primitivo.

Você pode criar um array em Java da seguinte maneira:

public class Main {

    public static void main(String[] args) {
        //  [] = new []
        char vowels[] = new char[5];

    }
}

Neste caso , comece digitando o tipo de dado que deseja armazenar no array  char . Em seguida, escreva o nome do array,  vowels seguido por um par de colchetes. Este par de colchetes indica ao Java que você está declarando um array de caracteres e não uma variável de caractere comum.

Em seguida, insira um sinal de igual seguido do  new operador usado para criar novos objetos em Java. Como arrays são tipos de referência em Java,  new é necessário para criar novas instâncias.

Finalize a declaração escrevendo o tipo novamente, seguido por outro par de colchetes delimitando o comprimento do array. Aqui,  5 significa que o array conterá cinco elementos e não mais que isso.

Ao trabalhar com uma única variável, você pode se referir a ela simplesmente pelo seu nome. Mas, no caso de um array, cada elemento terá um índice e os arrays são baseados em zero. Isso significa que o primeiro elemento em um array terá  0 como índice e não  1.

Para acessar um elemento em um array, comece escrevendo o nome do array – neste caso,  vowels seguido por um par de colchetes envolvendo o índice desejado. Portanto, se quiser acessar o primeiro elemento do array, faça o seguinte:

public class Main {

    public static void main(String[] args) {
        char vowels[] = new char[5];

        vowels[0] = 'a';
    }
}

Neste ponto,  vowels[0] é semelhante a uma variável de caractere comum. Você pode imprimi-la, atribuir um novo valor a ela, realizar cálculos no caso de tipos numéricos e assim por diante.

Como o array está vazio neste momento, estou atribuindo o caractere  a ao primeiro índice. Você pode atribuir o restante das vogais aos demais índices da seguinte maneira:

public class Main {

    public static void main(String[] args) {
        char vowels[] = new char[5];

        vowels[0] = 'a';
        vowels[1] = 'e';
        vowels[2] = 'i';
        vowels[3] = 'o';
        vowels[4] = 'u';

    }
}

Como os índices começam em ,  0 eles terminam no comprimento do array - 1, que neste caso é  4. Se você tentar atribuir outro elemento ao array,  vowels[4] = 'x'; o compilador gerará o seguinte erro:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
    at arrays.Main.main(Main.java:18)

Matrizes não podem ser impressas como variáveis comuns. Você terá que usar um laço (loop) ou converter a matriz em uma string. Como ainda não discuti laços (loops), usarei o segundo método.

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        char vowels[] = new char[5];

        vowels[0] = 'a';
        vowels[1] = 'e';
        vowels[2] = 'i';
        vowels[3] = 'o';
        vowels[4] = 'u';

        System.out.println("These are the vowels: " + Arrays.toString(vowels));

    }
}

Primeiro, você precisará importar  java.util.Arrays; e usar o  Arrays.toString() método para converter o array em uma string. Esta classe possui vários outros métodos interessantes, mas antes de discuti-los, gostaria de mostrar como declarar e inicializar um array de uma só vez.

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        char vowels[] = {'a', 'e', 'i', 'o', 'u'};

        System.out.println("These are the vowels: " + Arrays.toString(vowels));

    }
}

O lado esquerdo da sintaxe da declaração permanece inalterado. No entanto, após o operador de atribuição, em vez de usar,  new você escreve os elementos individuais do array separados por vírgula e entre chaves.

Nesse caso, o compilador contará o número de elementos no array e usará isso como o comprimento do array.

Se você não sabe o comprimento de um array, você pode dar uma olhada na  length propriedade.

Neste caso,  vowels.length será,  5 pois há cinco elementos no array. A  length propriedade é um inteiro e está presente em todos os arrays em Java.

Matrizes também podem ser multidimensionais. Até agora, você trabalhou com matrizes como esta:

Imagem

Matrizes unidimensionais como esta são perfeitas quando você deseja armazenar uma série de valores. Mas imagine a rotina médica diária de alguém na forma de uma tabela:

Imagem

A primeira linha representa os sete dias da semana e as colunas representam quantas vezes o paciente deve tomar o medicamento, das três vezes ao dia.  0 significa não e  1 significa sim.

Você pode mapear essa rotina usando uma matriz multidimensional em seu programa:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        int medicineRoutine[][] = {
                {1, 2, 3, 4, 5, 6, 7},
                {0, 1, 1, 0, 1, 1, 0},
                {1, 0, 1, 0, 1, 0, 0},
                {0, 0, 1, 1, 0, 1, 0},
        };

        System.out.println(Arrays.deepToString(medicineRoutine)); // [[1, 2, 3, 4, 5, 6, 7], [0, 1, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 0], [0, 0, 1, 1, 0, 1, 0]]

    }
}

Matrizes multidimensionais não podem ser impressas usando o  Arrays.toString() método regular; você precisa pesquisar mais a fundo.

Embora a saída não se pareça em nada com a tabela, você pode fazer com que ela pareça uma tabela usando alguma programação inteligente:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        int medicineRoutine[][] = {
                {1, 2, 3, 4, 5, 6, 7},
                {0, 1, 1, 0, 1, 1, 0},
                {1, 0, 1, 0, 1, 0, 0},
                {0, 0, 1, 1, 0, 1, 0},
        };

        System.out.println(Arrays.deepToString(medicineRoutine).replace("], ", "]
"));
    }
}

// [[1, 2, 3, 4, 5, 6, 7]
// [0, 1, 1, 0, 1, 1, 0]
// [1, 0, 1, 0, 1, 0, 0]
// [0, 0, 1, 1, 0, 1, 0]]

Você já aprendeu sobre o  replace() método para strings. Você está apenas substituindo a chaveta final em cada linha por uma chaveta e um caractere de nova linha.

A primeira linha representa os 7 dias da semana e as demais linhas são as rotinas médicas de cada dia. Cada linha nesta tabela representa uma matriz.

Para acessar um único valor de uma matriz multidimensional, você precisará de dois índices. O primeiro índice determina a linha e o segundo determina a coluna.

Então medicineRoutine[2][3]  , selecionaremos o elemento no índice  3 do terceiro array. Esse elemento será  0. Trabalhar com arrays multidimensionais pode parecer um pouco complicado, mas praticar tornará isso muito mais fácil.

Já que você pode criar arrays de qualquer tipo em Java, por que não tenta criar outros tipos de arrays você mesmo?

Como classificar uma matriz

Uma das tarefas mais comuns que você executará em arrays é classificá-los.  java.utils.Arrays O  Arrays.sort() método para fazer exatamente isso é:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        char vowels[] = {'e', 'u', 'o', 'i', 'a'};

        Arrays.sort(vowels);

        System.out.println("The sorted array: " + Arrays.toString(vowels)); // [a, e, i, o , u]

    }
}

Arrays.sort()  método recebe o array não classificado como argumento e o classifica no local. Assim, em vez de receber um novo array classificado como retorno, o array original será classificado em ordem crescente.

Por padrão, o método trata o primeiro índice do array como seu índice inicial e o comprimento do array como seu índice final.

Você pode especificar esses dois índices manualmente. Por exemplo, se quiser classificar apenas  u, o, i em ordem crescente e deixar  e, a como está, faça o seguinte:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        char vowels[] = {'e', 'u', 'o', 'i', 'a'};

        int startIndex = 1;
        int endIndex = 4;

        Arrays.sort(vowels, startIndex, endIndex);

        System.out.println("The sorted array: " + Arrays.toString(vowels)); // [e, i, o, u , a]

    }
}

Desta vez, o método usa o array como primeiro parâmetro, o índice inicial como segundo parâmetro e o índice final como terceiro parâmetro. O restante dos comportamentos permanece o mesmo de antes.

Como realizar uma pesquisa binária em uma matriz

Pesquisar valores dentro de um valor ordenado é outra tarefa comum. O  Arrays.binarySearch() método permite pesquisar itens em uma matriz ordenada usando o algoritmo de busca binária.

public class Main {

    public static void main(String[] args) {
        char vowels[] = {'a', 'e', 'i', 'o', 'u'};

        char key = 'i';

        int foundItemIndex = Arrays.binarySearch(vowels, key);

        System.out.println("The vowel 'i' is at index: " + foundItemIndex); // 2

    }
}

O  Arrays.binarySearch() método recebe um array como primeiro parâmetro e a chave de busca (também conhecida como o item que você está procurando) como segundo parâmetro. Ele retornará o índice do item encontrado como um inteiro.

Você pode armazenar esse índice em um  int e usá-lo para acessar o elemento do array como  vowels[foundItemIndex].

Observe que o array precisa ser ordenado em ordem crescente. Se não tiver certeza da ordem do array, use o  Arrays.sort() método para ordená-lo primeiro.

Por padrão, o método trata o primeiro índice do array como seu índice inicial e o comprimento do array como seu índice final. Mas você também pode especificar esses índices manualmente.

Por exemplo, se você quiser que a pesquisa ocorra de índice  2 para índice  4, você pode fazer isso da seguinte maneira:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        char vowels[] = {'a', 'e', 'i', 'o', 'u'};

        char key = 'i';
        int startIndex = 2;
        int endIndex = 4;

        int foundItemIndex = Arrays.binarySearch(vowels, startIndex, endIndex, key);

        System.out.println("The vowel 'i' is at index: " + foundItemIndex); // 2

    }
}

Desta vez, o método usa o array que você deseja pesquisar como o primeiro parâmetro, o índice inicial como o segundo parâmetro, o índice final como o terceiro parâmetro e a chave de pesquisa como o quarto parâmetro.

Agora, a busca ocorrerá em  i,  oe  u. Portanto, se você procurar por  a, ele não será encontrado. Caso o item fornecido não seja encontrado, você obterá um índice negativo. O índice negativo resultante variará com base em uma série de fatores, mas não entrarei em detalhes aqui. Se você tiver interesse em saber mais,  confira meu artigo sobre o assunto .

Como preencher uma matriz

Você já aprendeu a inicializar um array com valores, mas às vezes pode ser necessário preencher um array inteiro com o mesmo valor. O  Arrays.fill() método pode fazer isso por você:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        char vowels[] = {'e', 'u', 'o', 'i', 'a'};

        Arrays.fill(vowels, 'x');

        System.out.println("The filled array: " + Arrays.toString(vowels)); // [x, x, x, x, x]

    }
}

Assim como o  Arrays.sort() método,  Arrays.fill() ele também executa sua operação no local. Ele recebe seu array como primeiro parâmetro, o valor com o qual você deseja preencher o array como segundo parâmetro e atualiza o array original no local.

Este método também trata o primeiro índice como o índice inicial e o comprimento do array como o índice final. Você pode especificar esses índices manualmente da seguinte maneira:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        char vowels[] = {'e', 'u', 'o', 'i', 'a'};

        int startIndex = 1;
        int endIndex = 4;

        Arrays.fill(vowels, startIndex, endIndex, 'x');

        System.out.println("The filled array: " + Arrays.toString(vowels)); // [e, x, x, x, a]

    }
}

Desta vez, o método usa seu array como o primeiro argumento, o índice inicial como o segundo argumento, o índice final como o terceiro argumento e o preenchimento como o quarto argumento.

Como fazer cópias de um array

Como os arrays em Java são de tipos de referência, copiá-los usando o operador de atribuição pode causar algum comportamento inesperado.

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        int oddNumbers[] = {1, 3, 5};
        int copyOfOddNumbers[] = oddNumbers;

        Arrays.fill(oddNumbers, 0);

        System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers)); // [0, 0, 0]

    }
}

Embora você tenha feito alterações no array de origem, a cópia também as reflete. Isso acontece porque, quando você usa o operador de atribuição para copiar um array, a cópia faz referência ao array original na memória.

Para copiar corretamente um array, você pode usar o  Arrays.copyOf() método da seguinte maneira:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        int oddNumbers[] = {1, 3, 5};
        int copyOfOddNumbers[] = Arrays.copyOf(oddNumbers, oddNumbers.length);

        Arrays.fill(oddNumbers, 0);

        System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers)); // [1, 3, 5]

    }
}

O método recebe o array de origem como primeiro argumento e o comprimento desejado do novo array como segundo argumento. Se você quiser que o comprimento seja o mesmo, basta passar o comprimento do array original usando a  length propriedade .

Se você colocar um comprimento menor, qualquer valor depois dele será cortado e se você colocar um comprimento maior, os novos índices serão preenchidos com o valor padrão do tipo de dado da matriz.

Existe outro método  Arrays.copyOfRange() que pode copiar uma parte de uma matriz para uma nova:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        int oddNumbers[] = {1, 3, 5, 7, 9, 11, 13, 15};

        int startIndex = 2;
        int endIndex = 7;

        int copyOfOddNumbers[] = Arrays.copyOfRange(oddNumbers, startIndex, endIndex);

        System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers)); // [5, 7, 9, 11, 13]

    }
}

Este método usa o array de origem como seu primeiro argumento, depois o índice inicial e, finalmente, o índice final.

Lembre-se de que o índice final não é inclusivo. É por isso que 15 está ausente no novo array. Mas se você quiser incluir o último índice do array, use o comprimento do array original como índice final.

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        int oddNumbers[] = {1, 3, 5, 7, 9, 11, 13, 15};

        int startIndex = 2;
        int endIndex = oddNumbers.length;

        int copyOfOddNumbers[] = Arrays.copyOfRange(oddNumbers, startIndex, endIndex);

        System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers)); // [5, 7, 9, 11, 13, 15]

    }
}

Agora, o novo array também incluirá 15. Você também pode inserir um número maior que o comprimento do array de origem. Nesse caso, os índices recém-adicionados conterão o valor padrão do tipo de dado do array.

Como comparar dois arrays

Se você tentar verificar se dois arrays são iguais ou não em Java usando o operador relacional equal, você obterá alguns resultados inesperados.

public class Main {

    public static void main(String[] args) {        
        int oddNumbers1[] = {1, 3, 5, 7, 9, 11, 13, 15};
        int oddNumbers2[] = {1, 3, 5, 7, 9, 11, 13, 15};

        System.out.println(oddNumbers1 == oddNumbers2); // false
    }
}

Embora os dois arrays sejam idênticos, a saída do programa é  false. Como arrays são tipos de referência, o operador relacional verificará se são a mesma instância ou não.

Para comparar dois arrays em Java, você pode usar o  Arrays.equals() método:

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        int oddNumbers1[] = {1, 3, 5, 7, 9, 11, 13, 15};
        int oddNumbers2[] = {1, 3, 5, 7, 9, 11, 13, 15};

        System.out.println(Arrays.equals(oddNumbers1, oddNumbers2)); // true
    }
}

Entretanto, se você alterar apenas um elemento em qualquer uma dessas matrizes, a saída será,  false pois as matrizes não permanecerão mais idênticas.

Você também pode comparar matrizes multidimensionais, mas para isso você terá que usar o  Arrays.deepEquals() método em vez do método normal.

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {        
        int medicineRoutine[][] = {
                {1, 2, 3, 4, 5, 6, 7},
                {0, 1, 1, 0, 1, 1, 0},
                {1, 0, 1, 0, 1, 0, 0},
                {0, 0, 1, 1, 0, 1, 0},
        };

        int medicineRoutine2[][] = {
                {1, 2, 3, 4, 5, 6, 7},
                {0, 1, 1, 0, 1, 1, 0},
                {1, 0, 1, 0, 1, 0, 0},
                {0, 0, 1, 1, 0, 1, 0},
        };

        System.out.println(Arrays.deepEquals(medicineRoutine, medicineRoutine2)); // true
    }
}

Este método chama a si mesmo sempre que encontra um novo array dentro do array pai.

Esses foram alguns dos métodos mais comuns dentro da  java.util.Arrays classe. Você pode consultar  a documentação oficial  se quiser saber mais.

Como usar loops em Java

Se precisar repetir uma tarefa por um determinado número de vezes, você pode usar um loop. Os loops podem ser de três tipos: loops  for ,  for...each loops e  while loops.

Para Laço

Os loops for são provavelmente os tipos mais comuns de loops que você verá na internet.

Cada loop for consiste em três partes: a inicialização, a condição e a expressão de atualização. O loop ocorre em várias etapas.

Imagem

Se você quiser imprimir os números de 0 a 10 usando um loop for, você pode fazer isso da seguinte maneira:

public class Main {

    public static void main(String[] args) {
        for (int number = 0; number <= 10; number++) {
            System.out.println(number);
        }
    }
}

// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10

O fluxograma desse loop se parece com isso:

Imagem

  1. No início do loop, você inicializa um novo inteiro nomeado  number com o valor inicial de  0.
  2. Então você verifica se o  number é menor ou igual a 10  ou não.
  3. Se for menor ou igual a 10 , você executa a instrução dentro do bloco de loop e imprime o número no terminal.
  4. Em seguida, você atualiza a variável numérica incrementando seu valor em 1.
  5. O loop retorna para verificar se o valor do número ainda é menor ou igual ou não.

Enquanto o valor de number permanecer menor ou igual a 10, o loop continua. No momento em que o valor da  number variável se torna  11, o loop termina.

Você pode usar um loop for para percorrer um array da seguinte maneira:

public class Main {

    public static void main(String[] args) {
        int fibonacciNumbers[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55};

        for(int index = 0; index < fibonacciNumbers.length; index++) {
            System.out.println(fibonacciNumbers[index]);
        }
    }
}

O fluxograma para esse loop será o seguinte:

Imagem

Como o último índice do array é uma unidade a menos que seu comprimento, você executa o loop enquanto o índice for menor que o comprimento do array. No momento em que o índice se torna igual ao comprimento do array, você sai do loop.

Uma das coisas divertidas que você pode fazer com laços é imprimir tabuadas de multiplicação. Por exemplo, a tabuada de multiplicação para 5 será a seguinte:

public class Main {

    public static void main(String[] args) {
        int number = 5;

        for (int multiplier = 1; multiplier <= 10; multiplier++) {
            System.out.println(String.format("%d x %d = %d", number, multiplier, number * multiplier));
        }
    }
}

// 5 x 1 = 5
// 5 x 2 = 10
// 5 x 3 = 15
// 5 x 4 = 20
// 5 x 5 = 25
// 5 x 6 = 30
// 5 x 7 = 35
// 5 x 8 = 40
// 5 x 9 = 45
// 5 x 10 = 50

Os laços também podem ser aninhados. Isso significa que você pode colocar um laço dentro de outro. Você pode imprimir a tabuada de todos os números de 1 a 10 usando laços aninhados:

public class Main {

    public static void main(String[] args) {
        for (int number = 1; number <= 10; number++) {
            System.out.println(String.format("
multiplication table of %d", number));
            for (int multiplier = 1; multiplier <= 10; multiplier++) {
                System.out.println(String.format("%d x %d = %d", number, multiplier, number * multiplier));
            }
        }
    }
}

Eu não ousaria imprimir a saída aqui. Em vez disso, experimente o código você mesmo. Desenhe cada iteração do loop em um pedaço de papel para entender o que está acontecendo em cada etapa.

Loop para cada

Se você quiser iterar sobre uma coleção, como uma matriz, e executar alguma operação em cada elemento dessa coleção, você pode usar um loop for...each.

public class Main {

    public static void main(String[] args) {
        int fibonacciNumbers[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55};

        for(int number : fibonacciNumbers) {
            System.out.println(number);
        }
    }
}

No caso de um loop for-each, o tipo de item precisa específico ao tipo da coleção com a qual você está trabalhando. Aqui, o array é do tipo inteiro, então o item no loop é do tipo inteiro.

Isso executa a mesma tarefa que o loop for mostrado anteriormente. Mas, neste caso, você não precisa controlar o índice nem usar colchetes para acessar os elementos. Parece mais limpo e menos sujeito a erros.

Enquanto Laço

Se você quiser executar um monte de código até que uma determinada condição seja atendida, você pode usar um loop while.

Imagem

Não há etapas de inicialização ou atualização em um loop while. O que quer que aconteça, acontece dentro do corpo do loop. Se você reescrever o programa para imprimir a tabuada de 5 usando um loop while, ficará assim:

public class Main {

    public static void main(String[] args) {
        int number = 5;
        int multiplier = 1;

        while (multiplier <= 10) {
            System.out.println(String.format("%d x %d = %d", number, multiplier, number*multiplier));

            multiplier++;
        }
    }
}

// 5 x 1 = 5
// 5 x 2 = 10
// 5 x 3 = 15
// 5 x 4 = 20
// 5 x 5 = 25
// 5 x 6 = 30
// 5 x 7 = 35
// 5 x 8 = 40
// 5 x 9 = 45
// 5 x 10 = 50

Embora os loops while não sejam tão comuns quanto os loops para o mundo real, vale a pena aprender sobre eles.

Laço Do-While

O último tipo de loop que você aprenderá é o do-while. Ele meio que inverte a ordem do loop enquanto normal – então, em vez de verificar a condição antes de executar o corpo do loop, você executa o corpo do loop primeiro e depois verifica a condição.

Imagem

O código da tabuada de multiplicação implementada usando um loop do-while será o seguinte:

public class Main {

    public static void main(String[] args) {
        int number = 5;
        int multiplier = 1;

        do {
            System.out.println(String.format("%d x %d = %d", number, multiplier, number*multiplier));

            multiplier++;
        } while (multiplier <= 10);
    }
}

// 5 x 1 = 5
// 5 x 2 = 10
// 5 x 3 = 15
// 5 x 4 = 20
// 5 x 5 = 25
// 5 x 6 = 30
// 5 x 7 = 35
// 5 x 8 = 40
// 5 x 9 = 45
// 5 x 10 = 50

Laços do tipo "do-while" são muito úteis quando você precisa executar alguma operação até que o usuário insira uma entrada específica. Por exemplo, exiba um menu até que o usuário pressione a tecla "x".

Como trabalhar com listas de array em Java

Arrays em Java não são redimensionáveis. Depois de definir um comprimento para uma matriz, você não poderá alterá-lo de alguma forma. A  ArrayListclasse em Java atenua essas limitações.

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(9);

        System.out.println(oddNumbers.toString()); // [1, 3, 5, 7, 9]
    }
}

Para criar listas de matrizes, você precisará importar uma java.util.ArrayList classe no topo do seu arquivo de origem.

Em seguida, comece a escrever  ArrayListe, dentro de um par de sinais de menor que-maior, escreva o tipo de dado dos elementos. Em seguida, adicione o nome da própria lista de arrays, seguido pelo operador de atribuição e  new ArrayList<>().

Você não pode criar listas de arrays de tipos primitivos, então você terá que usar um wrapper de classe correspondente.

Embora esses elementos tenham índices de base zero, como matrizes, você não pode usar a notação de chaves quadradas para acessá-los. Em vez disso, você terá que usar o  get()método:

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(9);

        System.out.println(oddNumbers.get(2)); // 5
    }
}

O  get()método fornecerá o valor no índice fornecido. Assim como  get()você pode usar o  set()método para atualizar o valor de um elemento.

import java.time.LocalDate;
import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(9);

        oddNumbers.set(2, 55);

        System.out.println(oddNumbers.get(2)); // 55
    }
}

O primeiro parâmetro do  set()método é o índice e o segundo é o valor atualizado.

Não há nenhuma  lengthpropriedade como em um array, mas você pode usar o  size()método em qualquer lista de arrays para descobrir seu comprimento.

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(9);

        System.out.println(oddNumbers.size()); // 5
    }
}

Você pode remover elementos de uma lista de matriz usando o método remove:

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(9);

        oddNumbers.remove(Integer.valueOf(7));
        oddNumbers.remove(Integer.valueOf(9));

        System.out.println(oddNumbers.toString()); // [1, 3, 5]
    }
}

O  remove()método pode remover um elemento por valor ou por índice. Se você passar um valor inteiro primitivo para o método, ele removerá o elemento não fornecido.

Mas se você passar um objeto como este código, o método encontrará e excluirá o elemento fornecido. O  valueOf()método está presente em todas as classes wrapper e pode converter um valor primitivo em um tipo de referência.

Como adicionar ou remover vários elementos

Você já viu exemplos de métodos  add()e  remove(). Existem dois outros métodos  addAll()e  removeAll()para trabalhar com múltiplos elementos.

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);

        ArrayList<Integer> moreOddNumbers = new ArrayList<>();

        moreOddNumbers.add(7);
        moreOddNumbers.add(9);
        moreOddNumbers.add(11);

        oddNumbers.addAll(moreOddNumbers); // [1, 3, 5, 7, 9, 11]

        System.out.println(oddNumbers.toString());

        oddNumbers.removeAll(moreOddNumbers);

        System.out.println(oddNumbers.toString()); // [1, 3, 5]
    }
}

Ambos os métodos aceitam coleções como parâmetro. No código acima, você está criando duas listas de arrays separadas e unindo-as usando o  addAll()método .

Em seguida, você remove os elementos da segunda lista de arrays usando o  removeAll()método e a lista de arrays retorna ao seu estado original.

Você também pode remover todos os elementos de uma lista de matriz usando o  clear()método:

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);

        oddNumbers.clear();

        System.out.println(oddNumbers.toString()); // []
    }
}

O método não requer nenhum parâmetro e também não retorna nenhum valor. Ele apenas esvazia sua lista de arrays em uma única chamada.

Como remover elementos com base em uma condição

O  removeIf()método pode remover elementos de uma lista de matrizes se eles atenderem a uma determinada condição:

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>();

        for (int i = 0; i <= 10; i++) {
            numbers.add(i);
        }

        System.out.println(numbers.toString()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        numbers.removeIf(number -> number % 2 == 1);

        System.out.println(numbers.toString()); // [0, 2, 4, 6, 8, 10]
    }
}

O método recebe uma expressão lambda como parâmetro. Expressões lambda são como métodos sem nome. Eles podem receber parâmetros e trabalhar com eles.

Aqui, o  removeIf()método percorrerá uma lista de arrays e passará cada elemento para a expressão lambda como o valor da  numbervariável.

Então a expressão lambda verificará se o número fornecido é divisível por 2 ou não será retornado  trueou  falsecom base nisso.

Se a expressão lambda retornar  true, o  removeIf()método manterá o valor. Caso contrário, o valor será excluído.

Como clonar e comparar listas de array

Para fazer uma duplicata de uma lista de matrizes, você pode usar o  clone()método .

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>();

        for (int i = 0; i <= 10; i++) {
            numbers.add(i);
        }

        ArrayList<Integer> numbersCloned = (ArrayList<Integer>)numbers.clone();

        System.out.println(numbersCloned.equals(numbers)); // true
    }
}

O  clone() método retorna um objeto, então você terá que convertê-lo manualmente para uma lista de arrays adequada. Você pode comparar duas listas de arrays usando o equals()  método, assim como em arrays.

Como verificar se um elemento está presente ou se a lista de matrizes está vazia

Você pode usar o  contains() método para verificar se uma lista de array contém um determinado elemento ou não:

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(9);

        System.out.println(oddNumbers.isEmpty()); // false
        System.out.println(oddNumbers.contains(5)); // true
    }
}

Se você quiser verificar se uma lista de arrays está vazia ou não, basta chamar o  isEmpty() método nela e você receberá um booleano em retorno.

Como classificar uma lista de matriz

Você pode classificar uma lista de matrizes em ordens diferentes usando o  sort() método:

import java.util.ArrayList;
import java.util.Comparator;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(1);
        oddNumbers.add(9);
        oddNumbers.add(3);

        System.out.println(oddNumbers.toString()); [5, 7, 1, 9, 3]

        oddNumbers.sort(Comparator.naturalOrder());

        System.out.println(oddNumbers.toString()); [1, 3, 5, 7, 9]
    }
}

O  sort() método recebe um comparador como parâmetro. Um comparador impõe a ordem de classificação na lista do array.

Você pode classificar a lista de arrays na ordem reversa apenas alterando o comparador passado:

import java.util.ArrayList;
import java.util.Comparator;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(1);
        oddNumbers.add(9);
        oddNumbers.add(3);

        System.out.println(oddNumbers.toString()); // [5, 7, 1, 9, 3]

        oddNumbers.sort(Comparator.reverseOrder());

        System.out.println(oddNumbers.toString()); // [9, 7, 5, 3, 1]
    }
}

Os comparadores também têm outros usos, mas eles estão fora do escopo deste livro.

Como manter elementos comuns de duas listas de array

Imagine um cenário em que você tem duas listas de arrays. Agora você precisa descobrir quais elementos estão presentes em ambas as listas e remover o restante da primeira lista.

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);

        ArrayList<Integer> moreOddNumbers = new ArrayList<Integer>();

        moreOddNumbers.add(5);
        moreOddNumbers.add(7);
        moreOddNumbers.add(9);

        oddNumbers.retainAll(moreOddNumbers);

        System.out.println(oddNumbers.toString()); // [5]
    }
}

O  retainAll() método pode remover os elementos incomuns da primeira lista de arrays para você. Você precisará chamar o método na lista de arrays na qual deseja operar e passar a segunda lista de arrays como parâmetro.

Como executar uma ação em todos os elementos de uma lista de matriz

Você já aprendeu sobre loops nas seções anteriores. Bem, listas de arrays têm um  forEach() método próprio que recebe uma expressão lambda como parâmetro e pode executar uma ação em todos os elementos da lista de arrays.

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        ArrayList<Integer> oddNumbers = new ArrayList<>();

        oddNumbers.add(1);
        oddNumbers.add(3);
        oddNumbers.add(5);
        oddNumbers.add(7);
        oddNumbers.add(9);

        oddNumbers.forEach(number -> {
            number = number * 2;
            System.out.printf("%d ", number); // 2 6 10 14 18
        });

        System.out.println(oddNumbers.toString()); // [1, 3, 5, 7, 9]
    }
}

Da última vez, a expressão lambda que você viu era uma única linha – mas pode ser maior. Aqui, o  forEach() método percorrerá a lista do array e passará cada elemento para a expressão lambda como o valor da  number variável.

A expressão lambda multiplicará o valor fornecido por 2 e o imprimirá no terminal. No entanto, a lista de arrays original permanecerá inalterada.

Como trabalhar com mapas de hash em Java

Mapas de hash em Java podem armazenar elementos em pares chave-valor. Esse tipo de coleção é comparável a dicionários em Python e objetos em JavaScript.

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        System.out.printf(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0, berry=2.5, guava=1.5}
    }
}

Para criar mapas de hash, primeiro você terá que importar a  java.util.HashMap classe no topo do seu arquivo de origem.

Então você começa escrevendo  HashMap e, dentro de um par de sinais de menor que-maior, você escreverá o tipo de dado para a chave e o valor.

Aqui, as chaves serão strings e os valores serão doubles. Depois disso, o operador de atribuição, seguido por  new HashMap<>().

Você pode usar o  put() método para inserir um registro no mapa hash. O método recebe a chave como primeiro parâmetro e seu valor correspondente como segundo parâmetro.

Há também o  putIfAbsent() método que adiciona o elemento fornecido somente se ele ainda não existir no mapa de hash.

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        prices.putIfAbsent("guava", 2.9);

        System.out.println(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0, berry=2.5, guava=1.5}
    }
}

Você pode usar o  get() método para extrair um valor do mapa de hash. O método recebe a chave como parâmetro.

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        System.out.println(prices.get("banana")); // 1.000000
    }
}

Existe outra variação deste método. O  getOrDefault() método funciona assim,  get() mas se a chave fornecida não for encontrada, ele retornará um valor padrão especificado.

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        System.out.println(prices.getOrDefault("jackfruit", 0.0)); // 0.0
    }
}

O valor padrão deve corresponder ao tipo dos valores no mapa de hash. Você pode atualizar um valor em um mapa de hash usando o  replace() método:

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        prices.replace("berry", 2.8);

        System.out.printf(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0, berry=2.8, guava=1.5}
    }
}

Para remover elementos de um mapa hash, você pode usar o  remove() método apropriadamente chamado:

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        prices.remove("guava");

        System.out.printf(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0, berry=2.5}
    }
}

Se você precisar saber quantas entradas existem em um mapa hash, você pode fazer isso usando o  size() método:

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        System.out.println(prices.size()); // 5
    }
}

Por fim, se você quiser limpar um mapa de hash em Java, poderá fazer isso usando o  clear() método .

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        prices.clear();

        System.out.println(prices.toString()); // {}
    }
}

Assim como nas listas de arrays, o método não aceita nenhum argumento nem retorna nenhum valor.

Como inserir ou substituir vários elementos em um mapa de hash

Se você quiser colocar vários elementos em um mapa hash de uma só vez, você pode fazer isso usando o  putAll() método:

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        HashMap<String, Double> morePrices = new HashMap<>();

        prices.put("jackfruit", 2.9);
        prices.put("pineapple", 1.1);
        prices.put("tomato", 0.8);

        prices.putAll(morePrices);

        System.out.println(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0, berry=2.5, pineapple=1.1, tomato=0.8, guava=1.5, jackfruit=2.9}
    }
}

O método recebe outro mapa hash como parâmetro e adiciona seus elementos àquele para o qual o método foi chamado.

Você também pode usar o  replaceAll() método para atualizar vários valores em um mapa de hash.

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        prices.replaceAll((fruit, price) -> price * 2);

        System.out.println(prices.toString()); // {orange=3.6, banana=2.0, apple=4.0, berry=5.0, guava=3.0}
    }
}

O método substituir tudo itera sobre o hashmap e passa cada par de valores-chave para a expressão lambda.

O primeiro parâmetro da expressão lambda é a chave e o segundo é o valor. Dentro da expressão lambda, você executa suas ações.

Como verificar se um mapa de hash contém um item ou se está vazio

Você pode usar os métodos  containsKey() e  containsValue() para verificar se um mapa de hash contém um valor ou não.

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        System.out.println(prices.containsKey("banana")); // true
        System.out.println(prices.containsValue(2.5)); // true
    }
}

A diferença entre os dois métodos é que o  containsKey() método verifica se a chave fornecida existe ou não e o  containsValue() método verifica se o valor fornecido existe ou não.

E se você quiser verificar se um mapa hash está vazio ou não, você pode fazer isso usando o  isEmpty() método:

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        System.out.println(prices.isEmpty()); // false
    }
}

Como o método retorna um valor booleano, você pode usá-lo em instruções if-else.

Como executar uma ação em todos os elementos de um mapa de hash

Assim como as listas de matrizes, os mapas de hash também têm seu próprio  forEach() método que você pode usar para executar um loop no mapa de hash e repetir uma determinada ação em cada entrada.

import java.util.HashMap;

public class Main {
    public static void main (String[] args) {
        HashMap<String, Double> prices = new HashMap<>();

        prices.put("apple", 2.0);
        prices.put("orange", 1.8);
        prices.put("guava", 1.5);
        prices.put("berry", 2.5);
        prices.put("banana", 1.0);

        System.out.println("prices after discounts");

        prices.forEach((fruit, price) -> {
            System.out.println(fruit + " - " + (price - 0.5));
        });
    }
}

// prices after discounts
// orange - 1.3
// banana - 0.5
// apple - 1.5
// berry - 2.0
// guava - 1.0

O método percorre cada entrada e passa a chave e o valor para a expressão lambda. Dentro do corpo da expressão lambda, você pode fazer o que quiser.

Classes e Objetos em Java

Aqui está uma  definição útil de programação orientada a objetos :

POO (Programação Orientada a Objetos) é um paradigma de programação baseado no conceito de "objetos", que podem conter dados e código: dados na forma de campos (geralmente conhecidos como atributos ou propriedades) e código, na forma de procedimentos (geralmente conhecidos como métodos).

Imagine um sistema de gerenciamento de biblioteca onde os membros da biblioteca podem fazer login, dar uma olhada nos livros que já pegaram emprestado, solicitar novos e assim por diante.

Neste sistema, os usuários e os livros podem ser objetos. Esses objetos terão suas próprias propriedades, como nome e data de nascimento (no caso de um usuário) e título e autor no caso dos livros.

As classes em programação orientada a objetos são modelos para os objetos mencionados. Já discutimos as possíveis propriedades dos objetos usuário e livro.

Para criar uma nova  User classe, clique com o botão direito do mouse na  src pasta novamente. Em seguida, vá em  Novo > Classe Java , nomeie-a  Usere pressione Enter.

Mantendo as propriedades discutidas anteriormente em mente, seu código para a  User classe deve ser o seguinte:

import java.time.LocalDate;

public class User {
    String name;
    LocalDate birthDay;
}

Este  LocalDate é um tipo de dado de referência que representa uma data. Agora, volte ao  Main.java arquivo e crie uma nova instância desta classe:

import java.time.LocalDate;

public class Main {
    public static void main (String[] args) {
        User user = new User();

        user.name = "Farhan";
        user.birthDay = LocalDate.parse("1996-07-15");

        System.out.printf("%s was born on %s.", user.name, user.birthDay.toString()); // Farhan was born on 15th July 1996.
    }
}

Criar um novo usuário não é muito diferente de criar uma nova string ou array. Você começa escrevendo o nome da classe e, em seguida, o nome da instância ou do objeto.

Em seguida, você insere o operador de atribuição seguido pela  new palavra-chave e a chamada do construtor. O construtor é um método especial que inicializa o objeto.

O construtor inicializou as propriedades do objeto com valores padrões  null para todos esses tipos de referência.

Você pode acessar as propriedades do objeto escrevendo o nome do objeto seguido de um ponto e depois o nome da propriedade.

O  LocalDate.parse() método pode analisar uma data de uma string específica. Como  birthDay é um tipo de referência, você precisará usar o  toString() método para imprimir no console.

O que é um método?

As variáveis ou propriedades de uma classe descrevem o estado de seus objetos. Os métodos, por outro lado, descrevem o comportamento.

Por exemplo, você pode ter um método dentro de sua  User classe que calcule a idade do usuário.

import java.time.Period;
import java.time.LocalDate;

public class User {
    String name;
    LocalDate birthDay;

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }
}

Aqui, a  this palavra-chave representa a instância atual da classe. Comece escrevendo o tipo de retorno do método. Como a idade de um usuário é um inteiro, o tipo de retorno deste método será  int.

Após o tipo de retorno, escreva o nome do método, seguido por um par de parênteses.

Em seguida, escreva o corpo do método entre chaves. A  Period classe em Java expressa um período de tempo no sistema de calendário ISO-8601. O  LocalDate.now() método retorna a data atual.

Portanto, a  Period.between(this.birthDay, LocalDate.now()).getYears() chamada do método retornará a diferença entre a data atual e a data de nascimento em anos.

Agora, de volta ao  Main.java arquivo, você pode chamar este método da seguinte maneira:

import java.time.LocalDate;

public class Main {
    public static void main (String[] args) {
        User user = new User();

        user.name = "Farhan";
        user.birthDay = LocalDate.parse( "1996-07-15");

        System.out.printf("%s is %s years old.", user.name, user.age()); // Farhan a 26 years old.
    }
}

Os métodos também podem aceitar parâmetros. Por exemplo, se você quiser criar um método  borrow() para inserir novos livros na lista de livros emprestados deste usuário, faça o seguinte:

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {
    String name;
    LocalDate birthDay;

    ArrayList<String> borrowedBooks = new ArrayList<String>();

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }

    void borrow(String bookTitle) {
        this.borrowedBooks.add(bookTitle);
    }
}

De volta ao  Main.java arquivo, você pode chamar esse método da seguinte maneira:

public class Main {
    public static void main (String[] args) {
        User user = new User();

        user.name = "Farhan";

        user.borrow("Carmilla");
        user.borrow("Hard West");

        System.out.printf("%s has borrowed these books: %s", user.name, user.borrowedBooks.toString()); // Farhan has borrowed these books: [Carmilla, Hard West]
    }
}

Vamos criar uma classe para os livros também:

import java.util.ArrayList;

public class Book {
    String title;
    ArrayList<String> authors = new ArrayList<String>();
}

Os livros geralmente têm vários autores. Agora você pode criar uma nova instância do livro no  Main.java arquivo.

import java.time.LocalDate;

public class Main {
    public static void main (String[] args) {
        User user = new User();
        user.name = "Farhan";
        user.birthDay = LocalDate.parse( "1996-07-15");

        Book book = new Book();
        book.title = "Carmilla";
        book.authors.add("Sheridan Le Fanu");

        System.out.printf("%s is written by %s", book.title, book.authors.toString()); // Carmilla is written by [Sheridan Le Fanu]

    }
}

Agora vamos voltar ao  User.java arquivo e criar um relacionamento entre os usuários e os livros:

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {
    String name;
    LocalDate birthDay;

    ArrayList<Book> borrowedBooks = new ArrayList<Book>();

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }

    void borrow(Book book) {
        this.borrowedBooks.add(book);
    }
}

Em vez de usar uma lista de array de strings, agora você está usando uma lista de array de livros para armazenar os livros emprestados por este usuário.

Como o tipo de argumento do método mudou, você terá que atualizar o código no  Main.java arquivo adequadamente:

import java.time.LocalDate;

public class Main {
    public static void main (String[] args) {
        User user = new User();
        user.name = "Farhan";
        user.birthDay = LocalDate.parse( "1996-07-15");

        Book book = new Book();
        book.title = "Carmilla";
        book.authors.add("Sheridan Le Fanu");

        user.borrow(book);

        System.out.printf("%s has borrowed these books: %s", user.name, user.borrowedBooks.toString()); // Farhan has borrowed these books: [Book@30dae81]
    }
}

Tudo funciona bem, exceto pelo fato de que as informações do livro não foram impressas corretamente.

Espero que você se lembre do  toString() método. Quando você chama  user.borrowedBooks.toString() o compilador, ele percebe que os itens armazenados no arraylist são objetos ou tipos de referência. Então, ele começa a chamar os  toString() métodos dentro desses itens.

O problema é que não há uma implementação adequada de  toString() na sua  Book classe. Abra  Book.java e atualize o código da seguinte maneira:

import java.util.ArrayList;

public class Book {
    String title;
    ArrayList<String> authors = new ArrayList<String>();

    public String toString() {
        return String.format("%s by %s", this.title, this.authors.toString());
    }
}

O  toString() método agora retorna uma string bem formatada em vez da referência ao objeto. Execute o código novamente e, desta vez, a saída deverá ser  Farhan has borrowed these books: [Carmilla by [Sheridan Le Fanu]].

Como você pode ver, poder projetar seu software em torno de entidades da vida real o torna muito mais relacionável. Embora haja apenas uma lista de arrays e um monte de strings em jogo, parece que uma operação real de empréstimo de livros está acontecendo.

O que é sobrecarga de método?

Em Java, vários métodos podem ter o mesmo nome se seus parâmetros forem diferentes. Isso se chama sobrecarga de método.

Um exemplo pode ser o  borrow() método da  User classe. No momento, ele aceita um único livro como parâmetro. Vamos criar uma versão sobrecarregada que possa aceitar um array de livros.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.Arrays;

public class User {
    private String name;
    private LocalDate birthDay;
    private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getBorrowedBooks() {
        return this.borrowedBooks.toString();
    }

    User (String name, String birthDay) {
        this.name = name;
        this.birthDay = LocalDate.parse(birthDay);
    }

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }

    void borrow(Book book) {
        borrowedBooks.add(book);
    }

    void borrow(Book[] books) {
        borrowedBooks.addAll(Arrays.asList(books));
    }
}

O tipo de retorno e o nome do novo método são idênticos aos anteriores, mas este aceita um array de  Book objetos em vez de um único objeto.

Vamos atualizar o  Main.java arquivo para usar esse método sobrecarregado.

public class Main {
    public static void main (String[] args) {
        User user = new User("Farhan", "1996-07-15");

        Book book1 = new Book("Carmilla", new String[]{"Sheridan Le Fanu"});
        Book book2 = new Book("Frankenstein", new String[]{"Mary Shelley"});
        Book book3 = new Book("Dracula", new String[]{"Bram Stoker"});

        user.borrow(new Book[]{book1, book2});

        user.borrow(book3);

        System.out.printf("%s has borrowed these books: %s", user.getName(), user.getBorrowedBooks());
    }
}

Como você pode ver, o  borrow() método agora aceita um array de livros ou um único objeto de livro sem nenhum problema.

O que são construtores em Java?

Construtores são um tipo especial de método que existe em todas as classes e, sempre que você cria um novo objeto de uma classe, o compilador o chama.

Como o método é chamado durante a construção de um objeto, ele é chamado de construtor. Por padrão, um construtor atribui valores padrão a todas as suas propriedades.

Para substituir o construtor padrão, você precisa criar um novo método em suas classes com o mesmo nome da classe.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {
    public String name;
    public LocalDate birthDay;
    public ArrayList<Book> borrowedBooks = new ArrayList<Book>();

    User (String name, String birthDay) {
        this.name = name;
        this.birthDay = LocalDate.parse(birthDay);
    }

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }

    void borrow(Book book) {
        this.borrowedBooks.add(book);
    }
}

Agora que você tem um construtor, em vez de analisar a data de uma string no  Main.java arquivo, você pode fazer isso aqui.

Isso ocorre porque o formato do aniversário é uma preocupação da  User turma e a  Main turma não precisa se preocupar com isso.

O mesmo tratamento para a classe de livros também:

import java.util.ArrayList;
import java.util.Arrays;

public class Book {
    public String title;
    public ArrayList<String> authors = new ArrayList<String>();

    Book(String title, String[] authors) {
        this.title = title;
        this.authors = new ArrayList<String>(Arrays.asList(authors));
    }

    public String toString() {
        return String.format("%s by %s", this.title, this.authors.toString());
    }
}

Novamente, o tipo de  authors coleção não é uma preocupação do Main  classe. A maneira mais básica de trabalhar com um conjunto de valores em Java é com um array.

Então você receberá os nomes dos autores como uma matriz da  Main classe e criará uma lista de matrizes a partir deles na  Book classe.

Agora você terá que passar esses parâmetros para o construtor ao criar um novo usuário ou objeto de livro no  Main.java arquivo.

import java.util.ArrayList;

public class Main {
    public static void main (String[] args) {
        User user = new User("Farhan", "1996-07-15");

        Book book = new Book("Carmilla", new String[]{"Sheridan Le Fanu"});

        user.borrow(book);

        System.out.printf("%s has borrowed these books: %s", user.name, user.borrowedBooks.toString()); // Farhan has borrowed these books: [Carmilla, Hard West]
    }
}

Veja como já está mais limpo. Mas logo ficará melhor.

O que são modificadores de acesso em Java?

Você já viu a palavra-chave  public várias vezes. Este é um dos modificadores de acesso em Java.

Existem quatro modificadores de acesso em Java:

Tipo primitivo Classe Wrapper
Padrão Acessível dentro do pacote
Público Acessível em qualquer lugar
Privado Acessível dentro da classe
Protegido Acessível dentro da classe e subclasses

Por enquanto, discutirei os  Defaultmodificadores   de acesso  Public e  .  serão discutidos em uma seção posterior.PrivateProtected

Você já aprendeu sobre classes. Pacotes são coleções de várias classes separadas por sua funcionalidade.

Por exemplo, se você estiver criando um jogo, você pode colocar todas as classes relacionadas à física em um pacote separado e as relacionadas aos gráficos em outro.

Os pacotes estão fora do escopo deste livro, mas à medida que você continuar trabalhando em projetos cada vez maiores, você pegará o jeito deles.

O  Public modificador de acesso é bastante autoexplicativo. Essas variáveis, métodos ou classes podem ser acessados de qualquer outra classe ou pacote do seu projeto.

Aqueles  Private , por outro lado, são o oposto. Estão disponíveis apenas dentro da sua classe.

Veja a  User classe, por exemplo. O nome e a data de nascimento de um usuário não devem ser acessíveis externamente.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {
    private String name;
    private LocalDate birthDay;
    private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

    User (String name, String birthDay) {
        this.name = name;
        this.birthDay = LocalDate.parse(birthDay);
    }

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }

    void borrow(Book book) {
        borrowedBooks.add(book);
    }
}

Melhor assim. Atualize a  Book classe também para ocultar o título e as informações do autor do mundo exterior.

import java.util.ArrayList;
import java.util.Arrays;

public class Book {
    private String title;
    private ArrayList<String> authors = new ArrayList<String>();

    Book(String title, String[] authors) {
        this.title = title;
        this.authors = new ArrayList<String>(Arrays.asList(authors));
    }

    public String toString() {
        return String.format("%s by %s", this.title, this.authors.toString());
    }
}

Como as propriedades se tornaram privadas agora, a  System.out.println() linha no  Main.java arquivo não conseguirá acessá-las diretamente, o que causará um problema.

A solução para este programa é escrever métodos públicos que outras classes podem usar para acessar essas propriedades.

O que são os métodos Getter e Setter em Java?

Getters e setters são métodos públicos em classes usados para ler e escrever propriedades privadas.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {
    private String name;
    private LocalDate birthDay;
    private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

    public String getName() {
        return this.name;
    }

    public String getBirthDay() {
        return this.birthDay.toString();
    }

    public String getBorrowedBooks() {
        return this.borrowedBooks.toString();
    }

    User (String name, String birthDay) {
        this.name = name;
        this.birthDay = LocalDate.parse(birthDay);
    }

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }

    void borrow(Book book) {
        borrowedBooks.add(book);
    }
}

As  getName() variáveis e  getBorrowedBooks() são responsáveis por retornar o valor das   variáveis name e  .borrowedBooks

Na verdade, você nunca acessa a variável de aniversário a partir do  age() método, então um getter não é necessário.

Como o tipo da  borrowedBooks variável não é uma preocupação da  Main classe, o getter garante que o valor seja retornado no formato adequado.

Agora atualize o código no  Main.java arquivo para usar estes métodos:

public class Main {
    public static void main (String[] args) {
        User user = new User("Farhan", "1996-07-15");

        Book book = new Book("Carmilla", new String[]{"Sheridan Le Fanu"});

        user.borrow(book);

        System.out.printf("%s has borrowed these books: %s", user.getName(), user.getBorrowedBooks());
    }
}

Excelente. Ficou ainda mais limpo e fácil de ler. Assim como os getters, existem setters para escrever valores nas propriedades privadas.

Por exemplo, você pode querer permitir que o usuário altere seu nome ou data de nascimento. O  borrow() método já funciona como um setter para a  borrowedBooks lista de arrays.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {
    private String name;
    private LocalDate birthDay;
    private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setBirthDay(String birthDay) {
        this.birthDay = LocalDate.parse(birthDay);
    }

    public String getBorrowedBooks() {
        return this.borrowedBooks.toString();
    }

    User (String name, String birthDay) {
        this.name = name;
        this.birthDay = LocalDate.parse(birthDay);
    }

    int age() {
        return Period.between(this.birthDay, LocalDate.now()).getYears();
    }

    void borrow(Book book) {
        borrowedBooks.add(book);
    }
}

Agora você pode chamar o  setName() método com o nome que quiser atribuir ao usuário. Da mesma forma, o  setBirthDay() método pode definir a data de aniversário.

Você  Book também pode implementar alguns getters e setters para a classe.

import java.util.ArrayList;
import java.util.Arrays;

public class Book {
    private String title;
    private ArrayList<String> authors = new ArrayList<String>();

    public String getTitle() {
        return this.title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthors() {
        return this.authors.toString();
    }

    public void setTitle(String[] authors) {
        this.authors = new ArrayList<String>(Arrays.asList(authors));
    }

    Book(String title, String[] authors) {
        this.title = title;
        this.authors = new ArrayList<String>(Arrays.asList(authors));
    }

    public String toString() {
        return String.format("%s by %s", this.title, this.authors.toString());
    }
}

Agora você não pode acessar essas propriedades diretamente. Em vez disso, você terá que usar um dos getters ou setters.

O que é herança em Java?

Herança é outro grande recurso da programação orientada a objetos. Imagine que você tem três tipos de livros: os tradicionais, os e-books e os audiolivros.

Embora tenham semelhanças, como título e autor, também apresentam algumas diferenças. Por exemplo, os livros tradicionais e os e-books têm contagem de páginas, enquanto os audiolivros têm duração. Os e-books também têm formatos como PDF ou EPUB.

Portanto, usar a mesma classe para todos os três não é uma opção. Isso não significa que você precisará criar três classes separadas com pequenas diferenças. Você pode simplesmente criar classes separadas para e-books e audiolivros e fazer com que herdem as propriedades e métodos da  Book classe.

Vamos começar adicionando a contagem de páginas na  Book classe:

import java.util.ArrayList;
import java.util.Arrays;

public class Book {
    private String title;
    private int pageCount;
    private ArrayList<String> authors = new ArrayList<String>();

    Book(String title, int pageCount, String[] authors) {
        this.title = title;
        this.pageCount = pageCount;
        this.authors = new ArrayList<String>(Arrays.asList(authors));
    }

    public String length() {
        return String.format("%s is %d pages long.", this.title, this.pageCount);
    }

    public String toString() {
        return String.format("%s by %s", this.title, this.authors.toString());
    }
}

Como você não vai usar getters e setters nestes exemplos, uma limpeza pareceu uma boa ideia. O  length() método retorna o comprimento do livro como uma string.

Agora crie uma nova classe Java chamada  AudioBook e coloque o seguinte código nela:

public class AudioBook extends Book{
    private int runTime;

    AudioBook(String title, String[] authors, int runTime) {
        super(title, 0, authors);

        this.runTime = runTime;
    }
}

A  extends palavra-chave informa ao compilador que esta classe é uma subclasse da  Book classe pai. Isso significa que esta classe herda todas as propriedades e métodos da classe pai.

Dentro do  AudioBook método construtor, você define o tempo de execução do audiolivro, o que é bom, mas você também terá que chamar manualmente o construtor da classe pai.

A  super palavra-chave em Java se refere à classe pai, então super(title, 0, authors)  essencialmente chama o método construtor pai com os parâmetros necessários.

Como os audiolivros não têm páginas, definir a contagem de páginas como zero pode ser uma solução fácil.

Ou você pode criar uma versão sobrecarregada do Book  método construtor que não requer a contagem de páginas.

Em seguida, crie outra classe Java nomeada  Ebook com o seguinte código:

public class Ebook extends Book{
    private String format;

    Ebook(String title, int pageCount, String[] authors, String format) {
        super(title, pageCount, authors);

        this.format = format;
    }
}

Esta classe é em grande parte idêntica à  Bookclasse, exceto pelo fato de ter uma propriedade de formato.

public class Main {
    public static void main (String[] args) {
        Book book = new Book("Carmilla", 200, new String[]{"Sheridan Le Fanu"});
        Ebook ebook = new Ebook("Frankenstein", 220, new String[]{"Mary Shelley"}, "EPUB");
        AudioBook audioBook = new AudioBook("Dracula", new String[]{"Bram Stoker"}, 160);

        System.out.println(book.toString()); // Carmilla by [Sheridan Le Fanu]
        System.out.println(ebook.toString()); // Frankenstein by [Mary Shelley]
        System.out.println(audioBook.toString()); // Dracula by [Bram Stoker]
    }
}

Até agora, tudo está funcionando bem. Mas você se lembra do  length()método que escreveu na Book aula? Ele funciona para os livros comuns, mas não funciona nos e-books.

Isso ocorre porque a propriedade de contagem de páginas está marcada como  privatee nenhuma outra classe, exceto que  Bookela poderá acessá-la. O título também é uma private propriedade.

Abra o  Book.javaarquivo e marque as propriedades  titlee  pageCountcomo protegidos.

import java.util.ArrayList;
import java.util.Arrays;

public class Book {
    protected String title;
    protected int pageCount;
    private ArrayList<String> authors = new ArrayList<String>();

    Book(String title, int pageCount, String[] authors) {
        this.title = title;
        this.pageCount = pageCount;
        this.authors = new ArrayList<String>(Arrays.asList(authors));
    }

    public String length() {
        return String.format("%s is %d pages long.", this.title, this.pageCount);
    }

    public String toString() {
        return String.format("%s by %s", this.title, this.authors.toString());
    }
}

Isso os tornará acessíveis a partir das subclasses. Os audiolivros têm outro problema com o  length()método.

Os audiolivros não possuem contagem de páginas. Eles têm duração, e essa diferença invalidará o método de duração.

Uma maneira de resolver esse problema é substituir o  length()método.

Como substituir um método em Java

Como o nome sugere, substituir significa cancelar o efeito de um método substituindo-o por outra coisa.

public class AudioBook extends Book{
    private int runTime;

    AudioBook(String title, String[] authors, int runTime) {
        super(title, 0, authors);

        this.runTime = runTime;
    }

    @Override
    public String length() {
        return String.format("%s is %d minutes long.", this.title, this.runTime);
    }
}

Você sobrescreve um método da classe pai reescrevendo-o na subclasse. A  @Overridepalavra-chave é uma anotação. Anotações em Java são metadadas.

Não é obrigatório anotar o método dessa forma. Mas, se você fizer isso, o compilador saberá que o método anotado substituirá um método pai e garantirá que você esteja seguindo todas as regras de substituição.

Por exemplo, se você cometer um erro no nome do método e ele não corresponder a nenhum método do pai, o compilador informará que o método não está atualizando nada.

public class Main {
    public static void main (String[] args) {
        AudioBook audioBook = new AudioBook("Dracula", new String[]{"Bram Stoker"}, 160);

        System.out.println(audioBook.length()); // Dracula is 160 minutes long.
    }
}

Legal, não é? Ao sobrescrever um método em Java, lembre-se de que tanto o método original quanto o sobrescrito devem ter o mesmo tipo de retorno, o mesmo nome e os mesmos parâmetros.

Conclusão

Gostaria de agradecer ao fundo do meu coração pelo tempo que você dedicou à leitura deste livro. Espero que tenha aproveitado e aprendido todos os conceitos fundamentais de Java.

 

No código aberto, tenho certeza de que, para realmente fazer algo bem, é preciso envolver muitas pessoas. - 

Até a próxima, procure seguros e continue aprendend