Java é uma linguagem de programação amplamente usada para codificar aplicações Web. Ela tem sido uma escolha popular entre os desenvolvedores há mais de duas décadas, com milhões de aplicações Java em uso hoje. Java é uma linguagem multiplataforma, orientada a objetos e centrada em rede que pode ser usada como uma plataforma em si. É uma linguagem de programação rápida, segura e confiável para codificar tudo, desde aplicações móveis e software empresarial até aplicações de big data e tecnologias do servidor.
- Documentação
- Convenção de Código
- Tipos Primitivos
- Casting
- Para saber mais: a classe Scanner
- Classes e Objetos
- Modificadores de Acesso
- Herança
- Anotações no Java
- Interfaces
- Var
A documentação do Java, conhecida como JavaDoc, é uma referência completa para todas as classes, interfaces e métodos disponíveis na plataforma Java. Ela está disponível online e pode ser acessada neste site da Orcacle.
O JavaDoc é organizado em pacotes, que contêm diversas classes e interfaces relacionadas a alguma funcionalidade específica da linguagem. Cada classe e interface é documentada individualmente, com informações detalhadas sobre sua funcionalidade, uso e exemplos de código.
A convenção de código do Java é um conjunto de regras recomendadas para escrever código Java que é fácil de ler, entender e manter. Essas regras foram definidas pela Oracle, a empresa que mantém a linguagem Java, e são amplamente seguidas pela comunidade de desenvolvedores Java.
Aqui estão algumas das principais convenções de código do Java:
- Nomes de classes devem começar com letra maiúscula e usar a convenção PascalCase (também conhecida como Upper CamelCase).
- Exemplo:
MinhaClasse
- Exemplo:
- Nomes de métodos devem começar com letra minúscula e usar a convenção camelCase.
- Exemplo:
meuMetodo()
- Exemplo:
- Nomes de constantes devem ser totalmente em letras maiúsculas, separadas por underline.
- Exemplo:
MINHA_CONSTANTE
- Exemplo:
- Nomes de variáveis devem começar com letra minúscula e usar a convenção camelCase.
- Exemplo:
minhaVariavel
- Exemplo:
- Todas as linhas de código devem ter no máximo 80 caracteres de largura para facilitar a leitura.
- Recomenda-se usar espaços em branco para separar operadores, palavras-chave e elementos de controle de fluxo.
- Exemplo:
if (condicao) {
- Exemplo:
- Use comentários para documentar seu código, explicando o que ele faz e por que ele faz isso. Comentários devem ser claros e concisos.
Caution
Esse ponto anterior, referente aos comentários, é polêmico, pois muitas pessoas desenvolvedoras consideram que um bom código deve ser autoexplicativo. Se você utiliza nomes intuitivos e descritivos para suas variáveis e métodos, fica mais claro de entender o que está acontecendo no código, dispensando a necessidade de uso de comentários.
Essas são apenas algumas das convenções de código mais comuns no Java. Seguir essas regras pode tornar seu código mais fácil de ler e entender, o que pode economizar tempo e esforço no longo prazo.
É importante lembrar que as convenções de código são apenas recomendações , e não regras obrigatórias. No entanto, seguir essas convenções pode ajudar a criar um padrão consistente em todo o seu código Java e também torná-lo mais fácil de compartilhar e colaborar com outros desenvolvedores.
Em Java, assim como na maioria das linguagens de programação, existem os tipos primitivos, que são os tipos de dados mais básicos e fundamentais da linguagem. Eles são utilizados para representar valores simples e são definidos pela própria linguagem.
Java possui oito tipos primitivos diferentes: boolean, byte, char, short, int, long, float e double. Cada um desses tipos possui suas próprias características e faixa de valores permitidos, conforme será descrito a seguir.
-
boolean: O tipo boolean é utilizado para representar valores lógicos, podendo assumir apenas dois valores: true ou false. É utilizado em expressões condicionais, loops e outros casos onde se deseja avaliar se uma determinada condição é verdadeira ou falsa.
-
byte: O tipo byte é utilizado para representar valores numéricos inteiros de 8 bits. Ele possui uma faixa de valores de -128 a 127.
-
char: O tipo char é utilizado para representar caracteres individuais. Ele pode armazenar qualquer caractere Unicode e é representado por aspas simples ('').
-
short: O tipo short é utilizado para representar valores numéricos inteiros de 16 bits. Ele possui uma faixa de valores de -32.768 a 32.767.
-
int: O tipo int é utilizado para representar valores numéricos inteiros de 32 bits. É um dos tipos de dados mais utilizados para representar números inteiros em Java e possui uma faixa de valores de -2.147.483.648 a 2.147.483.647.
-
long: O tipo long é utilizado para representar valores numéricos inteiros de 64 bits. Ele é utilizado para representar valores inteiros muito grandes e possui uma faixa de valores de -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807.
-
float: O tipo float é utilizado para representar valores numéricos de ponto flutuante, ou seja, valores com casas decimais, sendo que ocupa 32 bits de memória. Ele pode representar números decimais com até sete dígitos e tem uma precisão limitada, o que significa que ele pode arredondar os números se eles forem muito grandes ou muito pequenos.
-
double: O tipo double é similar o float, entretanto ele ocupa 64 bits de memória e pode representar números decimais com até 15 dígitos.
Casting é um recurso utilizado em Java para converter um tipo de dado em outro. Essa conversão pode ser feita de forma automática pelo compilador (conversão implícita), quando o tipo de dado de destino é compatível com o tipo de dado de origem, ou de forma manual (conversão explícita), utilizando o operador de casting.
O casting é utilizado para permitir que tipos de dados incompatíveis possam ser utilizados em uma mesma operação ou expressão. Por exemplo, se um método espera um parâmetro do tipo int e o valor que se deseja passar é do tipo double, é necessário fazer um casting para converter o valor em int.
O casting implícito é realizado automaticamente pelo compilador quando o tipo de dado de origem é compatível com o tipo de dado de destino. Por exemplo, é possível atribuir um valor de tipo int a uma variável do tipo double, pois o tipo double é maior e suporta todos os valores que o tipo int pode armazenar:
int x = 10;
double y = x; // casting implícito
O casting explícito é realizado quando o tipo de dado de origem é incompatível com o tipo de dado de destino. Nesse caso, devemos utilizar o operador de casting para realizar a conversão:
double x = 10.5;
int y = (int) x; // casting explícito
No exemplo anterior, o valor da variável x é convertido em um valor inteiro utilizando o casting explícito. É importante notar que, neste caso, a parte decimal será descartada e o valor atribuído à variável y será 10.
Abaixo tem uma tabela, onde você pode visualizar mais facilmente as conversões que são implícitas e as que necessitam ser feitas de forma explícita.
A classe Scanner do Java é utilizada para ler dados de entrada em um programa Java. Esses dados podem ser lidos a partir de várias fontes de entrada, como arquivos, fluxos de entrada, Strings e até mesmo a entrada do usuário através do teclado, como vimos em aula.
Ela oferece uma série de métodos para ler dados de diferentes tipos, como inteiros, números de ponto flutuante, strings e caracteres.
Para utilizar a classe Scanner, primeiro é necessário importá-la no início do seu programa. Provavelmente ao incluir a mesma no código, a IDE já vai sugerir o import. Esse import ficará como descrito abaixo:
import java.util.Scanner;
Veja um exemplo básico de como utilizar a classe Scanner para ler dados distintos:
public class ExemploScanner {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Digite seu nome: ");
String nome = scanner.nextLine();
System.out.print("Digite sua idade: ");
int idade = scanner.nextInt();
System.out.print("Digite o valor que pretende investir esse mês: ");
double valor = scanner.nextDouble();
System.out.println(nome + " que tem " + idade + " anos, irá investir R$ " + valor + " esse mês.");
scanner.close();
}
}
Nesse exemplo, primeiro importamos a classe Scanner e, em seguida, criamos uma instância dela passando o objeto System.in
como parâmetro para indicar que queremos ler a entrada do usuário pelo teclado.
Depois, usamos o método nextLine()
para ler uma linha de texto. Além desse, utilizamos também o nextInt()
para ler um número inteiro e o nextDouble()
para ler um número decimal.
Você pode encontrar a lista completa de métodos na documentação oficial do Java.
Uma classe é uma estrutura que define um tipo de objeto. A classe é como um molde, que define quais são as características (atributos) e comportamentos (métodos) que os objetos desse tipo vão possuir. Por exemplo, podemos definir uma classe chamada Pessoa
que tenha os atributos nome
e idade
, além do método fazAniversario()
A sintaxe para definir essa classe em Java é a seguinte:
public class Pessoa {
String nome;
int idade;
void fazAniversario() {
idade++;
}
}
Já um objeto, é uma instância de uma classe, sendo por meio dele que conseguimos representar informações na aplicação, pois a classe serve apenas para padronizar os objetos, mas não para representar um objeto em si. Para criar um objeto em Java, precisamos utilizar a palavra reservada new seguida do nome da classe e de parênteses vazios. Por exemplo, para criar um objeto do tipo Pessoa, podemos escrever o seguinte código:
Pessoa pessoa1 = new Pessoa();
Nesse exemplo, criamos um objeto a partir da classe Pessoa, e guardamos na variável pessoa1 uma referência a ele. Como esse objeto foi criado como sendo do tipo Pessoa, ele possui os atributos nome e idade, além do método fazAniversario(). É a partir do objeto que podemos de fato representar uma pessoa, ou seja, preencher os atributos e chamar os métodos. Por exemplo:
Pessoa pessoa1 = new Pessoa();
pessoa1.nome = "Ana";
pessoa1.idade = 20;
System.out.println(pessoa1.nome + " tem " + pessoa1.idade + " anos");
pessoa1.fazAniversario();
System.out.println("A idade agora é: " +pessoa1.idade);
Podemos criar vários objetos do tipo Pessoa, sendo que todos eles terão os mesmos atributos e métodos, já que são da mesma classe, mas cada um pode possuir informações distintas. Por exemplo:
Pessoa pessoa1 = new Pessoa();
pessoa1.nome = "Ana";
pessoa1.idade = 20;
System.out.println(pessoa1.nome + " tem " + pessoa1.idade + " anos");
pessoa1.fazAniversario();
System.out.println("A idade agora é: " +pessoa1.idade);
Pessoa pessoa2 = new Pessoa();
pessoa2.nome = "Carlos";
pessoa2.idade = 20;
System.out.println(pessoa2.nome + " tem " + pessoa2.idade + " anos");
pessoa2.fazAniversario();
System.out.println("A idade agora é: " +pessoa2.idade);
Em Java, os modificadores de acesso são palavras-chave que definem o nível de visibilidade de classes, atributos e métodos, sendo que eles ajudam a garantir a segurança e encapsulamento do código.
Existem quatro tipos de modificadores de acesso em Java: public, protected, private e default (também conhecido como package-private).
O modificador de acesso public é o mais permissivo de todos. Uma classe, atributo ou método declarado como public pode ser acessado por qualquer classe em qualquer pacote. Ou seja, ele possui visibilidade pública e pode ser utilizado livremente. Por exemplo:
public class Conta {
public double saldo;
public void sacar(double valor) {
// lógica de saque...
}
}
public class Principal {
public static void main(String[] args) {
Conta c1 = new Conta();
c1.saldo = 300;
c1.sacar(100);
}
}
O modificador de acesso default é aquele que não especifica nenhum modificador de acesso. Quando nenhum modificador de acesso é especificado, a classe, atributo ou método pode ser acessado apenas pelas classes que estão no mesmo pacote. Por exemplo:
package br.com.alura.conta;
public class Conta {
default double saldo;
default void sacar(double valor) {
// lógica de saque...
}
}
package br.com.alura.testes;
public class Principal {
public static void main(String[] args) {
Conta c1 = new Conta();
c1.saldo = 300;
c1.sacar(100);
}
}
No código anterior, a classe Conta está em um pacote e a classe Principal em outro pacote distinto. A classe Conta pode ser instanciada dentro da classe Principal, pois ela possui o modificador de acesso public, entretanto, o atributo saldo e o método sacar tem o modificador default e, portanto, não podem ser acessados de dentro da classe Principal, o que vai causar um erro de compilação no código anterior.
O modificador de acesso private é o mais restritivo de todos. Uma classe, atributo ou método declarado como private só pode ser acessado dentro da própria classe. Ou seja, ele possui visibilidade restrita e não pode ser utilizado por outras classes. Por exemplo:
public class Conta {
private double saldo;
private void sacar(double valor) {
// lógica de saque...
}
}
public class Principal {
public static void main(String[] args) {
Conta c1 = new Conta();
c1.saldo = 300;
c1.sacar(100);
}
}
No código anterior, vai ocorrer erro de compilação na classe Principal
, pois o atributo saldo
e o método sacar
foram declarados como private, não podendo com isso serem acessados de fora da própria classe Conta
.
Existe ainda um último modificador de acesso, que é o protected, mas falaremos dele mais adiante no curso, após ser apresentado o conceito de herança de classes.
A herança é um conceito fundamental da orientação a objetos, sendo implementada em Java através da relação é um entre classes. Isso significa que uma classe pode herdar atributos e métodos de outra classe, tornando com isso o código mais reutilizável.
No Java, a herança é realizada através da palavra-chave extends. A classe que herda é chamada de subclasse, e a classe que é herdada é chamada de superclasse. A subclasse pode acessar todos os atributos e métodos públicos e protegidos da superclasse, além de poder sobrescrever os métodos da superclasse para criar comportamentos específicos.
Por exemplo:
public class Conta {
private String titular;
private double saldo;
public void sacar(double valor) {
if (valor <= 0) {
System.out.println("Valor deve ser maior do que zero!");
} else if (saldo >= valor) {
saldo -= valor;
System.out.println("Saque realizado com sucesso. Saldo atual: " +saldo);
} else {
System.out.println("Saldo insuficiente.");
}
}
public void depositar(double valor) {
if (valor > 0) {
saldo += valor;
System.out.println("Depósito realizado com sucesso. Saldo atual: " +saldo);
} else {
System.out.println("Valor deve ser maior do que zero!");
}
}
//getters e setters
}
public class ContaPoupanca extends Conta {
private double taxaDeJuros;
public void calcularJuros() {
double juros = this.getSaldo() * taxaDeJuros;
System.out.println("Juros atual: " +juros);
}
public void sacar(double valor) {
double taxaSaque = 0.01;
super.sacar(valor + taxaSaque);
}
//getters e setters
}
No código anterior, a classe Conta
é a superclasse e a classe ContaPoupanca
é a subclasse. A classe ContaPoupanca
herda os atributos e métodos da classe Conta
, e adiciona um novo atributo taxaDeJuros
e um novo método calcularJuros
. Embora os atributos sejam herdados, como eles foram declarados como private na superclasse, não poderão ser acessados diretamente na subclasse, devendo então serem utlizados os métodos getters/setter, que são públicos.
Repare também no código anterior que a subclasse sobrescreveu o método sacar, para que seja descontada a taxa de saque, além de utilizar a palavra chave super para chamar o método da superclasse, evitando com isso duplicar um código já existente. Essa é a grande vantagem da herança: reaproveitamento de código com flexibilidade para sobrescrever comportamentos.
Em Java, é importante notar que a herança múltipla não é permitida. A herança múltipla ocorre quando uma subclasse herda de duas ou mais superclasses. Por exemplo:
public class ContaPoupanca extends Conta, Pagamento {
//codigo da classe omitido
}
O código anterior não compila, pois o extends aceita apenas uma única classe, ou seja, uma classe pode ter apenas uma superclasse. Entretanto, é possível criar uma hierarquia de classes utilizando herança, simulando com isso uma herança múltipla. Por exemplo:
public class Conta {
//codigo da classe omitido
}
public class ContaCorrente extends Conta {
//codigo da classe omitido
}
public class ContaCorrentePessoaFisica extends ContaCorrente {
//codigo da classe omitido
}
No código anterior, a classe ContaCorrentePessoaFisica
está herdando de ContaCorrente
, que por sua vez herda da classe Conta
, ou seja, indiretamente a classe ContaCorrentePessoaFisica
vai herdar de Conta
, pois sua superclasse herda dela.
Ao usar herança no Java, temos a possibilidade de utilizar o modificador de acesso protected, que permite que os atributos de uma classe sejam acessados por outras classes do mesmo pacote e também por suas subclasses, independentemente do pacote em que se encontram.
O modificador protected é útil em situações em que uma classe precisa permitir que suas subclasses acessem diretamente seus atributos, sem a necessidade de restringir o acesso apenas pelos métodos getters e setters. Por exemplo, suponha que temos as seguintes classes em um projeto:
public class Conta {
private String titular;
private double saldo;
public void sacar(double valor) {
//implementacao do metodo omitida
}
public void depositar(double valor) {
//implementacao do metodo omitida
}
//getters e setters
}
public class ContaPoupanca extends Conta {
private double taxaDeJuros;
public void calcularJuros() {
double juros = this.getSaldo() * taxaDeJuros;
System.out.println("Juros atual: " +juros);
}
//getters e setters
}
No código anterior, repare que no método calcularJuros
, da classe ContaPoupanca
, o atributo saldo
não é acessado diretamente, pois ele foi declarado como private na classe Conta
, devendo então seu acesso ser feito pelo método getSaldo()
.
Podemos declarar o atributo saldo
como protected, para evitar essa situação e liberar o acesso direto a ele pelas classes que herdam da classe Conta
:
public class Conta {
private String titular;
protected double saldo;
public void sacar(double valor) {
//implementacao do metodo omitida
}
public void depositar(double valor) {
//implementacao do metodo omitida
}
//getters e setters
}
public class ContaPoupanca extends Conta {
private double taxaDeJuros;
public void calcularJuros() {
double juros = this.saldo * taxaDeJuros;
System.out.println("Juros atual: " +juros);
}
//getters e setters
}
Repare que agora o atributo saldo
foi acessado diretamente pela classe ContaPoupanca
.
As anotações, também conhecidas como annotations, são uma forma de adicionar configurações ao código Java de uma maneira bem simples. Elas são usadas para fornecer informações adicionais sobre o código, como o significado de uma classe, um método ou um atributo.
No Java, as anotações são definidas com o uso do símbolo "@" seguido do nome da anotação. Por exemplo, a anotação @Deprecated
é usada para indicar que um método ou classe está obsoleto e não deve ser mais utilizado. Outra anotação muito útil é a @Override
, conforme foi demonstrado no último vídeo, que indica que um método está sobrescrevendo o mesmo método definido na sua classe mãe.
Diversas especificações e frameworks Java, como Hibernate, Bean Validation e Spring, utilizam anotações. Por exemplo, no Bean Validation a anotação @NotNull
é usada para validar que um atributo não seja nulo.
As anotações são muito úteis e comuns em aplicações Java, sendo importante que você esteja familiarizado com esse recurso, pois certamente o utilizará bastante em seus projetos.
Em Java, interfaces são uma forma de definir um contrato que as classes devem seguir, sendo que ele define quais métodos devem ser implementados pelas classes que o implementarem. Interfaces permitem que diferentes classes possam ser tratadas de maneira padronizada, via polimorfismo, tornando assim o código fácil de estender com novos comportamentos.
No Java, uma interface é definida usando a palavra-chave interface. Por exemplo:
public interface Tributavel {
double getValorImposto();
}
No exemplo de código anterior, estamos definindo uma interface chamada Tributavel, sendo que ela possui apenas um método chamado getValorImposto()
que retorna um valor do tipo double
. Essa interface pode ser implementada por qualquer classe que queira ser tributável no projeto.
Para implementar uma interface, usamos a palavra-chave implements após a definição da classe. A classe que implementa a interface deve implementar todos os métodos definidos na interface. Por exemplo:
public class Produto implements Tributavel {
private String nome;
private double valor;
@Override
public double getValorImposto() {
return this.valor * 0.1;
}
//getters e setters
}
No exemplo anterior, estamos criando uma classe chamada Produto que implementa a interface Tributavel
. Essa classe implementa o método getValorImposto()
, que está definido na interface Tributavel
, com uma lógica de que o imposto do produto é igual a 10% do seu valor.
Também poderíamos ter uma classe de serviços, conforme abaixo:
public class Servico implements Tributavel {
private String descricao;
private double valor;
private double aliquotaISS;
@Override
public double getValorImposto() {
return this.valor * this.aliquotaISS / 100;
}
//getters e setters
}
No exemplo acima, estamos criando uma classe chamada Servico que implementa a interface Tributavel
. Essa classe implementa o método getValorImposto()
, que está definido na interface Tributavel
, com uma lógica de que o imposto do serviço é igual ao seu valor vezes a alíquota de ISS definida e dividido por 100. Então para um serviço de R$ 1.200,00 e alíquota de 5%, o método retornará: 1200 * 5 / 100, cujo valor do imposto fica R$ 60,00.
Interfaces podem ser utilizadas para definir comportamentos que podem ser aplicados a várias classes diferentes, tornando assim o código mais modular e fácil de manter.
Por exemplo, suponha que temos um sistema de vendas que precisa calcular o imposto de diferentes tipos de produtos. Podemos criar a interface Tributavel
, para definir o comportamento de calcular imposto, e criar várias classes diferentes que implementam essa interface para calcular o imposto de diferentes produtos. Por exemplo:
public class CalculadoraImposto {
private double totalImposto = 0;
public void calcularImposto(Tributavel item) {
this.totalImposto += item.getValorImposto();
}
public double getTotalImposto() {
return this.totalImposto;
}
}
Nesse exemplo, estamos criando uma classe chamada CalculadoraImposto, que tem um atributo privado chamado totalImposto
, que armazena o valor total dos impostos.
Repare que o método calcularImposto
recebe um parâmetro do tipo Tributavel
. Ao declarar uma variável com o tipo de uma interface, como é feito nesse método, podemos atribuir a essa variável qualquer objeto que implemente essa interface, ou seja, tanto um objeto do tipo Servico
quanto Produto
. Para ambos os casos, a CalculadoraImposto
irá chamar o método implementado na classe específica. Ou seja, para um produto, irá chamar o método getTotalImposto implementado na classe Produto. E para um serviço, irá chamar o método getTotalImposto implementado na classe Servico.
Isso é muito útil quando queremos tratar vários objetos de classes diferentes de forma semelhante, permitindo que diferentes classes possam ser tratadas de maneira padronizada, facilitando a manutenção e extensão do código. Esse é mais um exemplo de aplicação do polimorfismo em Java, mas agora com a utilização de interfaces.
A partir da versão 10 do Java, foi adicionada uma nova funcionalidade para a declaração de variáveis chamada var. Essa nova palavra-chave permite que o compilador infira automaticamente o tipo da variável com base no valor atribuído a ela. Isso pode tornar o código mais limpo e legível, além de reduzir a digitação de código redundante.
A sintaxe básica para declarar uma variável com var é a seguinte:
var nomeDaVariavel = valorInicial;
Onde nomeDaVariavel
é o nome que você quer dar à variável e valorInicial
é o valor que você quer atribuir a ela. O tipo da variável será inferido automaticamente pelo compilador com base no valor atribuído.
Exemplo:
var numero = 10;
A declaração de variáveis com var
possui algumas limitações:
- O tipo da variável deve ser inferido automaticamente pelo compilador. Isso significa que não é possível utilizar
var
em variáveis cujo tipo não possa ser inferido automaticamente. - Não é possível usar
var
em variáveis sem valor inicial. É necessário atribuir um valor à variável na mesma linha em que ela é declarada.
A declaração de variáveis com var
é uma funcionalidade relativamente nova no Java, mas que pode trazer diversos benefícios para o código, como a redução de digitação e melhor legibilidade do código. No entanto, é importante lembrar que existem limitações e que nem sempre é possível utilizar esse recurso.
Nesse exemplo, a variável numero será inferida como sendo do tipo int, já que o valor atribuído é um número inteiro.