Translate

quinta-feira, 31 de janeiro de 2013

Java 7 - Bloco multi-catch

No artigo anterior demonstrei o novo try capaz de encerrar recursos sem requerer do programador linhas de códigos improdutivas para isso.

Em sequência a produtividade no tratamento de exceções, vamos explorar o bloco multi-catch.

Do que se trata?

O Java 7 incorporou também uma forma de capturar mais de um tipo de exceção desde que as exceções capturadas herdem em algum momento de sua genealogia a mesma superclasse.

O exemplo a seguir lança propositalmente uma das exceções como IOException, IOError e InternalError. Estas exceções têm em comum Throwable como superclasse, por isso podem ser unidas em um único bloco capaz de capturar estas exceções.

Exemplo 1 - Testando o bloco multi-catch

import java.io.IOError;
import java.io.IOException;

public class TestaMultiCatch {

    public static final int MINIMUM = 1;
    public static final int MAXIMUM = 15;
   
    public static void main(String[] args) {

        try {

            int randomValue = MINIMUM + (int) (Math.random() * MAXIMUM);
           
            if (randomValue <= 5) {
                throw new IOException("Ocorrido IOException");
            } else if (randomValue <= 10) {
                throw new IOError(new Throwable("Ocorrido IOError"));
            } else {
                throw new InternalError("Ocorrido InternalError");
            }
           

        } catch(IOException | IOError | InternalError e) {

            System.out.println(String.format("Falha lançada para teste: %s", e.getMessage()));

        }

    }

}


O lançamento da exceção é aleatório porque um valor aleatório entre 1 e 15 é gerado para randomValue. Logo após, são realizados testes de períodos numéricos para lançar as exceções variavelmente.

Importante: Se a superclasse de uma das exceções do exemplo for adicionada no mesmo catch, a exceção deve ser removida porque será capturada pela sua superclasse.

Até breve.

Java 7 - Auto encerramento de recursos com novo try

Já comentei de algumas novidades do Java 7, mas não posso deixar de falar sobre o seu novo try capaz de gerenciar o encerramento automático de recursos.

Em versões anteriores ao Java 7, quando um recurso é construído e aberto (como ocorre com arquivos, conexões de banco de dados e sockets) algumas linhas de códigos a mais são necessárias para em caso de erro ainda garantir o fechamento adequado do recurso para não deixá-lo aberto ad eternum na memória do aplicativo.

Exemplo 1 - Teste do antigo try

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TestaOldTry {

    public static void main(String[] args) {

        FileReader fileReader = null;
        BufferedReader bufferReader = null;

        try {

            fileReader = new FileReader("c:/temp/datafile.txt");
            bufferReader = new BufferedReader(fileReader, 1024);

            String record = null;

            while ((record = bufferReader.readLine()) != null) {

                System.out.println(record);

            }     

        } catch(IOException e) {

            System.out.println(String.format("Falha ao ler arquivo: %s", e.getMessage()));

        } finally {

            if (bufferReader != null) {

                try {
                    bufferReader.close();
                } catch(IOException e) {
                    System.out.println("Falha ao fechar bufferReader: " + e.getMessage());
                }

            }

            if (fileReader != null) {

                try {
                    fileReader.close();
                } catch(IOException e) {
                    System.out.println("Falha ao fechar fileReader: " + e.getMessage());
                }

            }

        }

    }

}


Observe o cuidado e excessividade de códigos empregados para garantir o fechamento dos recursos bufferReader e fileReader no bloco finally.

Eis a novidade...

No Java 7 foi incorporado um try opcional para gerenciar automaticamente o encerramento de recursos. Para isso ocorrer, é necessário conferir se a classe do recurso implementa a nova interface java.lang.AutoCloseable ou a interface java.io.Cloaseable, senão o novo try não funciona logo na compilação. Este comportamento impede o programador para não se esquecer de programar um método de encerramento no recurso a ser automaticamente encerrado pelo novo try.

Agora vamos ver como fica a utilização do novo try, também conhecido por try-with-resources.

Exemplo 2 - Teste do novo try

public class TestaNewTry {

    public static void main(String[] args) {

        try (FileReader fileReader = new FileReader("c:/temp/datafile.txt");
                BufferedReader bufferReader = new BufferedReader(fileReader, 1024)) {

            String record = null;

            while ((record = bufferReader.readLine()) != null) {

                System.out.println(record);

            }     

        } catch(IOException e) {

            System.out.println(String.format("Falha ao ler arquivo: %s", e.getMessage()));

        }
    }

}


Este novo código revela um algoritmo bem menor e ainda traz o mesmo efeito do algoritmo do primeiro exemplo.

O objetivo com este novo try é certamente reduzir o número de códigos de sua programação Java para torná-la mais produtiva.

Até mais.

quinta-feira, 8 de novembro de 2012

Java - Os rumos desta linguagem

Hoje vamos falar de algo mais descontraído, vamos refletir um pouco sobre os novos rumos da linguagem Java sem pretensão de ser dono da razão ou de ser imparcial, mas expor honestamente a percepção das ocorrências mais concretas definidoras dos rumos atuais desta linguagem "amada demais" e "cornetada" pelos efeitos colaterais de sua grandiosidade.

A finalidade é perceber o cenário atual, acertos, erros e colaborações necessárias para a continuidade desta história.

Vamos nessa.

Como a maioria dos programadores da linguagem Java já sabe, ela foi feita para atender a máxima "faça uma vez, execute em qualquer lugar".

Desde sua concepção, implantação, popularização e amadurecimento, o Java fez jus a sua máxima no que diz respeito a servidores, estações de trabalhos e computadores aplicados dotados de Linux. O Java se desenvolveu nos campos do JSE (Java Standard Edition) e JEE (Java Enterprise Edition).

Também deixou muito a desejar na sua proposta JME (Java Micro Edition) segregada conforme as limitações do hardware em CLDC e MIDP/CDC, o que exigia técnicas e precauções diferentes para complicar ainda mais o trabalho com JME até sua segunda versão. Hoje, o JME propõe maior integração e simplificação em sua terceira geração, mas convenhamos, o Android já chegou e será preciso um esforço extra para o JME 3.0 emplacar.

Prefiro focar no quadro geral e afirmar: apesar da Sun não ter tido sucesso em simplificar o JME e até mesmo não ter perfil para tê-lo difundido nos atuais smartphones, precisamos reconhecer o grande sucesso do Java nas plataformas empresariais o que não é pouco. Muitos criticam por achar que plataforma empresarial se limita a empresas, mas sem a "ortodoxia", plataforma empresarial é basicamente a ciência tecnológica a serviço das demandas de uma sociedade econômica de extrema exigência. Significa que o Java esteve até agora presente em muitos ciclos econômicos auxiliando uma Era da Informação por sua capacidade de integração transparente sobre modernos sistemas distribuídos.

O Java se consolidou e talvez parte de seus defensores e militantes estejam pensando no fim de um episódio de mais uma linguagem de programação. Todavia, o Java da Sun Microsystems não será mais o mesmo desde ter ficado órfão depois da saída de seu pai, James Gosling, da Sun. Isso está longe de ser uma intenção de provocar nostalgia nos defensores do Java, porque o Java se expandiu para uma JVM múlti linguagem em sua versão 7 e ainda expandiu para o Android quando a Google se inclinou a sua praticidade. Agora o Java não é mais encabrestado pela Sun e uma concorrência saudável já foi iniciada.

Esta alteração nas fronteiras Java não é obra do acaso, talvez seja obra de competição entre empresas, mas está marcada por um novo estágio da Era da Informação compreendido pelo domínio público do conhecimento promovido pelas redes sociais digitais e ainda pelo paradigma da Computação nas Nuvens. 

As empresas Google e Oracle deixam seus interesses e posições bastante claros neste novo cenário e vão produzir um Java cada vez melhor.

Fontes:
http://www.infoq.com/br/articles/invokedynamic
https://blogs.oracle.com/jrose/resource/pres/201107-JDK7.pdf
http://www.slideshare.net/amsterdatech/linguagens-dinamicas-na-jvm
http://www.amsterdaintelligence.blogspot.com.br/2010/03/mundo-das-linguagens-o-futuro-da.html
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

domingo, 4 de novembro de 2012

Java - Evitar NullPointerException

O NullPointerException é um erro comum num programa escrito em Java quando se opera uma variável do tipo objeto sem antes verificar se está nula.

Algumas estratégias sugerem usar um padrão de produção do objeto que ao invéz de retornar um valor nulo é retornado um objeto que simboliza não ter encontrado o objeto esperado. Exemplo: new ContatoNaoEncontrado("Not found").

Essa sugestão é ruim porque requer a criação de uma classe adicional para simbolizar o caso de não encontrado e ainda torna frustrante a codificação porque o fluxo esperado do programador pode ser enganado caso ele se descuide e deixe de verificar se o objeto retornado representa "not found" (não encontrado).

Acredito que não exista uma fórmula mágica para evitar todas as verificações de nulidade, mas nem por isso devemos deixar de acreditar numa solução que simplifica bastante esta tarefa em casos corriqueiros onde se trabalha com valores mais simples como é o caso das operações de números ou textos em termos de atributo ou variável simples para apoiar a execução de um método.

A função sugerida aqui é para simplificar operações de atributos e até mesmo realizar ETL de forma ágil e limpa, pois isso não é errado e reduz o volume de códigos na situação explicada. Aplicar um padrão de projeto nesses casos não convém e seria impraticável algum padrão de projeto.


Como a maior parte dos programas trata valores numericos e alfanuméricos em grande volume, elaborei esta técnica na tentativa de contribuir com a boa prática de programação. Ela consiste na criação de um método chamado ANPE (Avoid NullPoiterException ou Evitar NullPointerException) que recebe dois valores. Se o primeiro valor for nulo, então retorna o segundo valor.

Vamos à prática.

AvoidNullPointerException

A classe AvoidNullPointerException conterá o método ANPE do qual falei. Ele é estático e utiliza Java Generic para tornar a função flexível ao tratar todos os objetos. Coloque a classe dentro de uma package br.com.wt.util para facilitar o exercício.

package br.com.wt.util;
public class AvoidNullPointerException {
   
    public static <V> V ANPE(V object, V object2) {

        if (object == null) {
            return object2;
        }
       
        return object;

    }
       
}


Testando

O teste é simples, pois se value for nulo, então o tornaremos 0L (zero). A importação da função estática ANPE diminui o trabalho para invocá-la:

import static br.com.wt.util.AvoidNullPointerException.ANPE;

public class Test {
   
    public static void main(String[] args) {

        Long value = null;

        if (ANPE(value, 0L) == 1L) {
            value = 100L;
        }

    }
       
}


Este teste pode ser realizado com String, Integer, Short, Double, Float e outros tipos de objetos desde que se esteja testando um valor.

Mais um teste para fixar

Agora com uma String.

import static br.com.wt.util.AvoidNullPointerException.ANPE;

public class Test {
   
    public static void main(String[] args) {

        String country = null;

        if (ANPE(country, "").equals("Brazil")) {
            System.out.println("World Cup 2014");
        } else {
            System.out.println("But will 2016 Olympics in Brazil");
        }
       
    }
       
}


Boa programação.

quinta-feira, 1 de novembro de 2012

Java - Tutorial para manipulação binária

Em 2009, elaborei um tutorial para ajudar alguns colegas de trabalho a entederem sobre manipulação binária de uma maneira mais rápida a partir da liguagem Java, pois este assunto é muito complicado dominar sem ajuda. Como obtive ajuda de diversos colegas para entender este assunto ao longo dos anos, busquei compilar boa parte do assunto neste tutorial naquela ocasião.

Neste tutorial são abordados os assuntos como operações binárias, operadores AND (e), OR (ou), NAND (não é), NOR (não ou), XOR (disjunção de valores quando dois valores comparados são diferentes), operadores bit-a-bit (bitwise) e ordem binária (Big-Endian e Little-Endian).

Publiquei o tutorial para manipulação binária em Java em: http://goo.gl/X6hka

Qualquer dúvida adicional sobre este tutorial, envie um comentário.

quarta-feira, 31 de outubro de 2012

Java 7 - Notação binária

O Java 7 incorpora uma notação bastante interessante e importante para expressar bits. Esta notação é chamada de Binary Literals e tem como prefixo 0b. Desde antes, o Java já suportava a notação hexadecimal (0x) e octal (0).

Como a maioria já sabe, todos os dados computacionais são bits em sua forma bruta. O tipo de uma variável é uma formatação dos bits para torná-los inteligíveis à nós.

Gostei desta notação porque me pouparia de acessar a calculadora científica (agora do programador) para converter uma máscara de bits em decimal ou hexadecimal. Espero ter a oportunidade de usufruir deste recurso no futuro.

Vamos explorar agora algumas possibilidades de uso do Binary Literals ou Notação Binária em Java 7:

Obs.: Aproveitei para usar "_" (underscore ou underline) para melhorar a leitura dos bits grandes, mas não interfere no resultado.

char A = 0b0000000001000001;

char B = 0b0000000001000010;

char C = 0b0000000001000011;

char D = 0b0000000001000100;

char E = 0b0000000001000101;

short numberShort_16bits = 0b0000000000000011;

int numberInt_32bits = 0b01000000_10000000_10000000_10000000;

long numberLong_64bits = 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L;

float numberFloat_32bits = 0b001000000_10000000_10000000_10000000;

double numberDouble_64bits = 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L;

char arrayOfChar [] = {0b0000000001000001, 0b0000000001000010, 0b000000000100001};

short arrayOfnumberShort_16bits [] = {0b0000000000000011, 0b0000000100000111};

int arrayOfnumberInt_32bits [] = {0b01000000_10000000_10000000_10000000, 0b01000000_10000000_10000000_10000000};

long arrayOfnumberLong_64bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L,
        0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};

float arrayOfnumberFloat_32bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L,
        0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};

double arrayOfnumberDouble_64bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L, 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};


Até a próxima.

quinta-feira, 27 de setembro de 2012

Java - Bufferizar dados

Após algumas vezes programar rotinas para capturar bytes vindos de conexão socket ou porta serial, armazenar estes bytes e em seguida processá-los para extrair a informação original, percebi logo a complexidade disso.

Qualquer um que já tenha programado um client ou server socket em Java, C++ e C ANSI sabe o que digo, pois utilizar um byte array para armazenar (bufferizar) dados recebidos antes de enquadrar uma informação significativa é uma maneira arcaica e dispensiosa.

Certo dia, resolvi simplificar esta situação com a criação de um mecanismo chamado WTByteBuffer bastante simples para prover apenas dois métodos, um método de escrita e outro de leitura a partir do ByteBuffer do Java NIO.

O método de escrita armazena os bytes lidos e o método de leitura consome os bytes. Desta forma, o programador Java será poupado de todo o trabalho discutido anteriormente para bufferizar e consumir dados recebidos.

WTByteBuffer - Classe para bufferizar


import java.nio.ByteBuffer;

public class WTByteBuffer {

    public static final int DEFAULT_MAX_SIZE = 1024 * 65;

    private volatile ByteBuffer buffer;
    private int size;
    private int bufferMaxSize;

    public int getSize() {
        return this.size;
    }

    public ByteBuffer getBuffer() {
        return this.buffer;
    }
   
   
    public WTByteBuffer() {
        this(WTByteBuffer.DEFAULT_MAX_SIZE);
    }

    public WTByteBuffer(int bufferMaxSize) {
        this.bufferMaxSize = bufferMaxSize;
        this.buffer = ByteBuffer.allocate(bufferMaxSize);
        buffer.position(0);
        this.size = 0;
       
    }

    public synchronized void write(byte[] bytes) {

        if (this.buffer == null)
            System.out.println("Buffer nulo : W");
       
        this.buffer.put(bytes);
        this.size = this.buffer.position();
       
    }

    public synchronized int read(byte[] bytes) {

        int target, position, newPosition, capacity;
        byte[] completeBuffer = null;
        byte[] remainingBuffer = null;

        if (bytes == null || bytes.length == 0) {
            return 0;
        }

        if (this.buffer == null)
            System.out.println("Buffer nulo : R");

        target = bytes.length;
        position = this.buffer.position();
        capacity = this.buffer.capacity();

        if (target > position) {
            target = position;
        }

        newPosition = position - target;
        completeBuffer = new byte[position];
        remainingBuffer = new byte[newPosition];
       
        this.buffer.flip();
        this.buffer.get(completeBuffer, 0, position);
        System.arraycopy(completeBuffer, 0, bytes, 0, target);

        this.buffer = null;
        this.buffer = ByteBuffer.allocate(capacity);
       
        if (newPosition > 0) {
            System.arraycopy(completeBuffer, target, remainingBuffer, 0, newPosition);
            this.buffer.put(remainingBuffer);
        }
   
        this.size = this.buffer.position();
   
        return target;

    }
   
    public synchronized void clear() {
       
        if (this.buffer != null) {
            this.buffer.clear();
            this.buffer = null;
        }
        this.buffer = ByteBuffer.allocate(bufferMaxSize);
        buffer.position(0);
        this.size = 0;

    }   
 

}

Testando um buffer

Este exemplo cria uma instância de WTByteBuffer com 100 bytes de capacidade, escreve a série de bytes de um texto qualquer e recupera de um em um byte. Depois repete o teste recuperando de dois em dois bytes do texto bufferizado.

        WTByteBuffer buffer = new WTByteBuffer(100);
        String text = "ABXYZ-0123456-abc";
       
        byte[] read = new byte[2];
        byte[] serialized = text.getBytes();
        int size = 0;

        System.out.println("-----------------------------------------");

        buffer.write(serialized);

        System.out.format("Texto guardado em buffer: %s\n", text);
       
        System.out.println("Recupera texto de 1 em 1 byte...");
       
        while (buffer.getSize() > 0) {

            read = new byte[1];

            size = buffer.read(read);

            System.out.format("1 byte lido: %s\n", new String(read));

        }

        System.out.format("Bytes guardados: %d\n", buffer.getSize());

        System.out.println("-----------------------------------------");

        buffer.write(serialized);

        System.out.format("Texto guardado em buffer: %s\n", text);
       
        System.out.println("Recupera texto de 2 em 2 byte...");
       
        while (buffer.getSize() > 0) {

            read = new byte[2];

            size = buffer.read(read);

            System.out.format("%d byte(s) lido(s): %s\n", size, new String(read));

        }
       
        buffer = null;



Numa situação real a capacidade deste buffer deverá ser maior do que 100. Caso desejar aumentar o tamanho, faça isso ao construir o WTByteBuffer ou então utilize o construtor padrão para iniciar com o tamanho padrão de reserva máxima.

A classe abordada ainda provê métodos adicionais para controle e acesso ao buffer central.

Boa sorte.