You are currently browsing the category archive for the ‘Java’ category.

Palavras-chave: Java, hash, MD5, SHA-1, SHA-256, MessageDigest

A maneira mais comum de se gerar um código hash em Java é utilizando a classe java.security.MessageDigest. O método a seguir gera o hash MD5 de uma string:

MessageDigest md = MessageDigest.getInstance("MD5");
md.update(frase.getBytes());
byte[] hashMd5 = md.digest();

O hash é retornado como um array de bytes, que pode ser impresso utilizando o método citado em um post anterior:

private static String stringHexa(byte[] bytes) {
   StringBuilder s = new StringBuilder();
   for (int i = 0; i < bytes.length; i++) {
       int parteAlta = ((bytes[i] >> 4) & 0xf) << 4;
       int parteBaixa = bytes[i] & 0xf;
       if (parteAlta == 0) s.append('0');
       s.append(Integer.toHexString(parteAlta | parteBaixa));
   }
   return s.toString();
}

Além do MD5, podemos também usar outros algoritmos de hash normalmente presentes nas distribuições da JVM. O exemplo a seguir gera os códigos MD5, SHA-1 e SHA-256 da mesma mensagem:

public static byte[] gerarHash(String frase, String algoritmo) {
  try {
    MessageDigest md = MessageDigest.getInstance(algoritmo);
    md.update(frase.getBytes());
    return md.digest();
  } catch (NoSuchAlgorithmException e) {
    return null;
  }
}
public static void main(String[] args) {
  String frase = "Quero gerar códigos hash desta mensagem.";
  System.out.println(stringHexa(gerarHash(frase, "MD5")));
  System.out.println(stringHexa(gerarHash(frase, "SHA-1")));
  System.out.println(stringHexa(gerarHash(frase, "SHA-256")));
}

A saída deste programa será:

51408e7592917e916b55f8021a0f7ad3
5907736c4de6fdd70fcdc7517c3aebd257a1addc
ed40c896d779f946956483d45e9efe08aac6373935d354c2be2136bf05bca42c

Palavras-chave: Java, Java 5, coleções, iteração, genéricos

A linguagem Java já tem mais de uma década de idade e naturalmente evoluiu neste período. Verifique se o jeito que você usa coleções está atualizado.

Antes do Java 1.2, a classe para representar arrays dinâmicos era a java.util.Vector:

Vector ps = new Vector();
ps.addElement(new Pessoa("Maria 1.1"));
for (Enumeration e = ps.elements(); e.hasMoreElements();)
  System.out.println(((Pessoa) e.nextElement()).getNome());

O Java 1.2 trouxe as classes ArrayList, LinkedList, HashMap, HashSet, que, em conjunto com outras, formaram o Collections Framework, uma biblioteca padronizada e bastante completa para lidar com coleções de valores. Algumas das novidades:

  • Os nomes de métodos ficaram mais curtos.

  • A hierarquia de classes e interfaces está mais bem formada e útil. Por exemplo, LinkedList e ArrayList podem ser generalizadas como List.

  • Foram feitas algumas otimizações de performance. Por exemplo, ArrayList não tem todos os métodos synchronized, como Vector.

Exemplificando:

List ps = new ArrayList();
ps.add(new Pessoa("Maria 1.2"));
for (Iterator i = ps.iterator(); i.hasNext();)
  System.out.println(((Pessoa) i.next()).getNome());

No Java 1.5 (ou Java 5), além das evoluções normais da API, houve também algumas mudanças na própria linguagem:

List<Pessoa> ps = new ArrayList<Pessoa>();
ps.add(new Pessoa("Maria 1.5"));
for (Pessoa p : ps)
  System.out.println(p.getNome());

Como se vê, as coleções podem agora usar tipos genéricos e há uma nova estrutura de for, chamada for-each.

Portanto, se de vez em quando você ainda se encontra usando Vector, procure atualizar-se para poder evoluir junto com a linguagem daqui para frente.

Palavras-chave: java, permgen, falta de memória, geração permanente

A máquina virtual Java tem uma área de memória limitada chamada Geração Permanente (Perm Generation), onde são armazenados objetos cuja desalocação é rara (ex.: código de classes, objetos de reflexão, pool de strings etc). Dado que esta área de memória é limitada, seu uso intenso pode gerar erros com a seguinte mensagem:

java.lang.OutOfMemoryError: PermGen space

Como a mensagem diz, isto acontece porque não há mais espaço na memória reservada para a Geração Permanente. Para que este problema não aconteça mais, use a opção -XX:MaxPermSize na inicialização da JVM. Isso aumentará o tamanho máximo da memória de Geração Permanente, como no exemplo a seguir:

java -XX:MaxPermSize=128m pacote.MinhaClasse

Neste caso, configuramos o tamanho máximo da memória de geração permanente para 128 megabytes, sendo que o tamanho padrão é 64 megabytes.

Uma demonstração pode ser feita com o código a seguir:

public class TestePerm {
   public static void main(String[] args) {
      int numeroDeAlocacoes = 0;
      try {
         System.out.println("Alocando...");
         ArrayList list = new ArrayList();
         String grandeString = new
            BigDecimal(1000).pow(1000).toString();
         for (int i = 0; i < 1000000; i++)
            list.add((grandeString + numeroDeAlocacoes++).intern());
         System.out.println("Fim.");
      } catch (OutOfMemoryError e) {
         System.out.println(e);
         System.out.println("Alocacoes antes do erro: " + numeroDeAlocacoes);
      }
   }
}

Este programa usa o método intern() da classe String para alocar strings no pool da JVM e fazer estourar a memória de geração permanente.

A seguir, vemos dois exemplos de execuções do programa TestePerm:

$ java codare.TestePerm
Alocando...
java.lang.OutOfMemoryError: PermGen space
Alocacoes antes do erro: 11087
$ java -XX:MaxPermSize=128m codare.TestePerm
Alocando...
java.lang.OutOfMemoryError: PermGen space
Alocacoes antes do erro: 22183

Na primeira execução, com o MaxPermSize configurado para 64 megabytes (tamanho padrão), conseguimos colocar apenas 11087 strings no pool antes de acabar a memória permanente. No segundo caso, com 128 megabytes de MaxPermSize, conseguimos fazer 22183 alocações.

A área de geração permanente é usada intensivamente em servidores de aplicação, pois estes executam milhares de classes e ficam meses no ar. Portanto, em um ambiente de produção, nunca se esqueça de configurar um tamanho máximo adequado para a memória permanente da JVM.

Palavras-chave: java, byte, conversão, hexadecimal

Em Java, as conversões e promoções numéricas às vezes fazem tarefas simples parecerem complicadas. A seguir, um método rápido para imprimir um array de bytes.

public class ImpressaoBytes {
    public static void main(String[] args) {
        System.out.println(paraStringHexa(
            new byte[] { 0x0F, 0x7f, 0x3d, 0x00, -0x23 }));
    }

    private static String paraStringHexa(byte[] bytes) {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            int parteAlta = ((bytes[i] >> 4) & 0xf) << 4;
            int parteBaixa = bytes[i] & 0xf;
            if (parteAlta == 0) s.append('0');
            s.append(Integer.toHexString(parteAlta | parteBaixa));
        }
        return s.toString();
    }
}

Para cada byte, o truque é simples: deslocamos para a direita, desprezamos o que não seja a quadra menos significativa (& 0xf) e deslocamos de volta para a esquerda; para a parte baixa, é só desprezar o que não for a quadra menos significativa.

Então basta usar o conversor de int para string hexa. A saída do programa será:

0f7f3d00dd

Uma provável tentativa seria substituir o laço por:

for (int i = 0; i < bytes.length; i++) {
    s.append(Integer.toHexString(bytes[i]));
}

Mas isso falharia ao se tentar imprimir bytes “negativos”, como o último de nosso array de exemplo. A saída incorreta seria:

f7f3d0ffffffdd
Add to Technorati Favorites

Assuntos