JavaRush /Blogue Java /Random-PT /Pausa para café #165. Pacotes em Java. Métodos thread-saf...

Pausa para café #165. Pacotes em Java. Métodos thread-safe para iniciantes

Publicado no grupo Random-PT

Pacotes Java

Fonte: Usemynotes Este post irá ajudá-lo a entender melhor os pacotes em Java, entender seu propósito e como implementá-los. Pausa para café #165.  Pacotes em Java.  Métodos thread-safe para iniciantes - 1

O que são pacotes em Java

Um pacote em Java é uma forma de agrupar um grupo de classes, interfaces e subpacotes. Os pacotes são usados ​​para criar grupos de classes, interfaces, enumerações relacionadas e assim por diante. Subpacotes são pacotes que estão dentro de outro pacote. Eles não são importados por padrão, mas você pode importá-los manualmente, se necessário. A especificação de acesso não é fornecida aos membros individuais de um subpacote; eles são tratados como pacotes separados.

Alguns tipos de pacotes em Java:

  • java.lang - vem junto com Java por padrão.
  • java.io - contém classes, métodos e outros elementos relacionados à entrada/saída.

Por que os pacotes são necessários?

  • Para evitar conflitos de nomes.
  • Para fornecer acesso controlado.
  • Para conseguir o encapsulamento de dados.

Como criar um pacote em Java?

Vamos criar um pacote chamado computador . Normalmente, o nome do pacote é escrito em letras minúsculas. Isso é feito apenas para evitar conflitos de nomes com nomes de classes. Também criaremos uma interface chamada Pluggable , que estará localizada no pacote do computador .
package computer;

interface Pluggable {
   public void pluggedIn();
   public void pluggedOut();
}
Agora criaremos uma classe chamada PenDrive que implementará a interface acima.
package computer;

public class PenDrive implements Pluggable {

   int storage = 64;

   public void pluggedIn () {
     System.out.println("Pen Drive is connected");
   }

   public void pluggedOut () {
     System.out.println("Pen Drive is removed");
   }

   public int storage() {
     return storage;
   }

   public static void main(String args[]) {
     PenDrive p = new PenDrive ();
     p.pluggedIn();
     System.out.println("Pen Drive Storage: " + p.storage());
     p.pluggedOut();
   }
}

Como criar uma hierarquia de pacotes em Java?

Ao formar uma hierarquia, os pacotes em Java são nomeados na ordem inversa. Isso os torna muito semelhantes a diretórios ou pastas. Assim como em um computador pessoal, onde uma pasta pode conter uma ou mais subpastas, o mesmo se aplica aos pacotes em Java. Vejamos um pacote chamado Asia.India.Kolkata . Todas essas são pastas existentes, mas se você considerar a geografia, fica claro que Calcutá fica na Índia e a Índia fica na Ásia. O principal objetivo de uma hierarquia é tornar as classes mais fáceis de encontrar.

Tipos de pacotes em Java

Pacotes integrados

Pacotes integrados são pacotes que consistem em um grande número de classes integradas que fazem parte da API Java. Esses pacotes incluem:
  • java.util - Este pacote contém um número finito de classes utilitárias que são usadas para implementar estruturas de dados, como lista vinculada, conjuntos e assim por diante. Ele também oferece suporte a operações de data e hora e muito mais.
  • java.net – Contém classes usadas para operações de rede.

Pacotes definidos pelo usuário

Os pacotes definidos pelo usuário são chamados de pacotes do usuário. O usuário pode criar manualmente um pacote e ter quantas classes desejar.

Como acessar um pacote de outro pacote?

Você pode acessar um pacote de outro pacote de três maneiras simples:
  • Usando um asterisco em uma instrução de importação
O caractere asterisco ( * ) é usado para representar “todas as coisas” em Java. Com ele, podemos importar tudo o que está dentro de um subpacote de um pacote. Exemplo: Considere um pacote chamado tools . Se quisermos importar tudo o que está dentro deste pacote, precisaremos usar a instrução import como:
import tools.*;
  • Usando import package.ClassName;
Mencionar o nome da classe em um pacote é uma forma eficaz de importar apenas as classes necessárias para o seu programa, evitando assim a importação de classes desnecessárias. Exemplo: Considere um pacote chamado books . Se quisermos importar apenas uma classe ou interface específica dela (veremos a classe Pages ), então podemos importar apenas isso usando:
import book.Pages;
  • Usando seu nome completo
Existe uma maneira de usar diretamente um pacote Java ou suas classes usando seu nome completo. Dessa forma você não precisa importar o pacote e pode utilizá-lo diretamente no programa. Exemplo:
java.awt.List aSimpleList = new java.awt.List();

Tamanho de lote padrão em Java

Por padrão, Java importa o pacote java.lang . Possui muitas classes que são comumente usadas em programas Java simples, como String , Integer e outros. Uma das classes mais importantes é a classe Object , que por sua vez também é encontrada no pacote java.lang . O tamanho deste pacote é baseado em seus componentes: 8 interfaces, 37 classes, 3 enums, 27 exceções, 23 tipos de erro e 5 tipos de anotação.

Métodos Java Thread-Safe para iniciantes

Fonte: Medium Usando este artigo, você pode aprender sobre o funcionamento de métodos thread-safe em Java. Pausa para café #165.  Pacotes em Java.  Métodos thread-safe para iniciantes - 2Descobri que muitos desenvolvedores Java júnior/médio não entendem como os métodos thread-safe devem funcionar em projetos reais. E como normalmente trabalhamos em um ambiente multithread, seu uso correto é muito importante. Um método thread-safe é um método que pode ser chamado de vários threads simultaneamente, sem afetar o estado dos dados um do outro. A compreensão insuficiente deste conceito leva a bugs que são difíceis de encontrar e corrigir. Para evitar tais erros, vejamos exemplos.

Exemplo 1:

public static int countLetters(String input) {
    int counter = 0;

    for (Character c : input.toCharArray()) {
        if (Character.isAlphabetic(c)) {
            counter++;
        }
    }

    return counter;
}
  • O método countLetters é estático, retorna um valor int e aceita um parâmetro string como entrada.
  • Um contador de variável primitivo é criado dentro do método, então o loop percorre os caracteres da string de entrada e incrementa o contador de variável cada vez que um caractere de letra é encontrado.
Este método é thread-safe? Muitos desenvolvedores dizem que não, porque neste caso temos uma operação de incremento que não é thread-safe. Vamos descobrir. No modelo de memória Java, temos pilha e heap. Cada thread tem sua própria pilha e todos os threads compartilham o mesmo heap. Nesse sentido, os dados da pilha são sempre thread-safe, mas os dados do heap não. A pilha armazena primitivas e referências de objetos. O heap contém os próprios objetos. Isso significa que neste exemplo de código, cada thread armazena seu próprio contador de variável na pilha e não tem nenhum efeito nos dados de outros threads, portanto o método é thread-safe . Observe que o valor String de entrada também é um objeto, mas String e wrappers primitivos ( Integer , Long , Double , Boolean e assim por diante) são thread-safe porque são imutáveis.

Exemplo #2:

public static int countLetters2(String input) {
    List<Character> listCounter = new ArrayList<>();

    for (Character c : input.toCharArray()) {
        if (Character.isAlphabetic(c)) {
            listCounter.add(c);
        }
    }

    return listCounter.size();
}
Este código usou a mesma lógica do primeiro exemplo, mas usou um objeto List em vez de uma variável int primitiva . Da parte anterior sabemos que os objetos em Java são armazenados em heap e List é um objeto. Também sabemos que a pilha armazena referências a objetos na pilha. No exemplo nº 2, cada thread cria um novo objeto ArrayList : e a variável listCounter armazena uma referência ao seu objeto no heap, para que nenhum outro thread possa alterar esse objeto.
List<Character> listCounter = new ArrayList<>();
Isso significa que este método é thread-safe.

Exemplo #3:

public class CounterUtil { // singleton

    List<Character> listCounter = new ArrayList<>();

    public int countLetters3(String input) {
        for (Character c : input.toCharArray()) {
            if (Character.isAlphabetic(c)) {
                listCounter.add(c);
            }
        }

        return listCounter.size();
    }
}
Neste exemplo, temos uma classe singleton (isso é importante) CounterUtil com uma variável global listCounter . Esta variável é criada ao mesmo tempo que a instância singleton. Quando vários threads chamam o método countChars3 , todos eles usam a mesma variável global listCounter , que armazena uma referência ao mesmo objeto no heap, e os threads lá influenciarão uns aos outros. Portanto, podemos concluir que este método não é thread-safe. E mesmo se mudarmos List<Character> listCounter para uma variável primitiva int counter , também não será thread-safe porque todos os threads usarão a mesma variável primitiva.

Último exemplo:

public static int countLetters4(List<Character> inputList) {
    List<Character> listCounter = new ArrayList<>();

    for (Character c : inputList) {
        if (Character.isAlphabetic(c)) {
            listCounter.add(c);
        }
    }

    return listCounter.size();
}
O método countLetters4 aceita uma lista de caracteres em vez de um parâmetro String . Aqui não podemos garantir que este método seja thread-safe. Por que? Porque não podemos ter certeza de como os desenvolvedores usarão esse método. Se outro thread externo alterar os dados em inputList ao mesmo tempo que nosso método counterLetters4 , isso poderá afetar o resultado final.

Conclusão

Vimos apenas quatro exemplos e eles não cobrem todos os aspectos da segurança de thread em projetos Java, mas são um bom ponto de partida. Na próxima vez que você vir um método em seu código, pergunte-se: “Este método é thread-safe?” E muito em breve você entenderá claramente a resposta.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION