Java com café: 2010

Introdução ao J2ME

Olá! No post de hoje pretendo mostrar como iniciar no desenvolvimento de aplicações para dispositivos móveis. Vou mostrar como preparar o ambiente de desenvolvimento e também como fazer um pequeno programa.


O Ambiente

A primeira coisa a se fazer é baixar o Sun Java Wireless Toolkit, também conhecido como J2ME Wireless Toolkit. Esse kit de ferramentas é utilizado para criar aplicações que serão executadas em dispositivos móveis com a tecnologia Java. A instação do toolkit não tem segredo, é só na base do next next finish...
O próximo passo é instalar a IDE. Para o exemplo que vou mostrar, vamos utilizar o Eclipse 3.5. Espero que já esteja familiarizado com ele.
E para integrar o toolkit e a IDE vamos utlizar o plugin EclipseME. A instalção do plugin é simples:

  1. Feche o Eclipse caso esteja aberto;
  2. Descompacte o arquivo do plugin;
  3. Recorte as pastas features e plugins que foram descompactadas;
  4. Cole as pastas na pasta onde encontra-se o eclipse.exe (vai perguntar se quer mesclar as pastas, responda sim);

Aliás, esses passos servem para instalação offline de qualquer plugin para Eclipse.
Pronto, agora só mais algumas configurações e o ambiente estará preparado. Como essas configurações dependem da criação de um novo projeto, vamos pra próxima parte!


Criando um projeto

Abra o Eclipse. Clique com o botão direito dentro do Package Explorer e New > Other...


Na janela que abrir navegue por J2ME e escolha J2ME Midlet Suite, então clique em Next:


Agora escolha o nome do projeto e clique em Next novamente:


Neste momento será solicitado que configure o dispositivo que será usado para executar a aplicação. Então clique em Manage Devices...


Outra janela será aberta, porém ainda sem nenhum dispositivo. Clique em Import...


Agora clique em Browse... e selecione a pasta WTK2.5.2_01 (ou outra versão disponível) que se encontra na raiz do disco (geralmente em C:, só não estará neste local se você tiver mudado durante a instalação). Clique em OK:


De volta na janela Import Devices clique em Refresh. Uma pesquisa será feita na pasta do toolkit e alguns dispositivos devem aparecer na lista:


Clique em Finish e voltamos na janela de gerenciamento com um dispositivo padrão selecionado:


Somente um dispositivo pode ser selecionado. Deixe o padrão e clique em OK. Então voltamos na janela de propriedades do projeto, mas agora com os dois combobox preenchidos:


Em device podemos selecionar DefaultGrayPhone ou DefaultColorPhone. Deixe o padrão que é DefaultGrayPhone e clique em Finish.

Pronto, agora temos um projeto criado e podemos iniciar o desenvolvimento.


Programando em J2ME

Vamos criar nosso primeiro programa. Abra a pasta do projeto no Package Explorer e clique com o botão direito no pacote src, escolha New > Other...


Dentro de J2ME escolha J2ME Midlet e clique em Next. Digite o nome HelloWorld e clique em Finish. Então será criada uma classe parecida com esta:
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class HelloWorld extends MIDlet {

public HelloWorld() {
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
}

protected void pauseApp() {
}

protected void startApp() throws MIDletStateChangeException {
}

}

O que é MIDlet? MIDlet é como são chamados os aplicativos em J2ME. Aplicativos Java para web são Applets, para servidores são Servlets e para dispositivos móveis são MIDlets. Na verdade as coisas são um pouco mais complicadas, mas a grosso modo podemos definir assim.
Observe que nossa classe estende MIDlet e por isso foram herdados 3 métodos:

  • startApp: É chamado quando se inicia a aplicação (equivalente ao main em J2SE). Também é chamado quando a aplicação é despausada.
  • pauseApp: É chamado quando o aplicativo fica em segundo plano (por exemplo, quando o usuário atende uma ligação)
  • destroyApp: É executado quando o aplicativo é finalizado e pode ser usado para liberar recursos, entre outras coisas.

Também temos o construtor que é uma espécie de método chamado quando a classe é instanciada (você já deve saber disto). Mas vamos ao que interessa. Declare a seguinte variável de instância:
Display d;
Deve aparecer um erro devido ao import, apenas peça que o Eclipse corrija pra você.
Cada MIDlet deve ter uma referência a uma variável do tipo Display. Este objeto pode recuperar informações sobre a tela (ex. quantidade de cores) e também é usado para exibir objetos.
No J2ME temos uma superclasse chamada Displayable da qual todos os outros objetos estendem. Objetos como Alert, Form ou TextBox estendem de Displayable, portanto eles SÃO Displayable. O objeto Display pode exibir qualquer tipo de Displayable. Veja como é a hierarquia destas classes:



Somemte um objeto Displayable pode ser exibido por vez. Então usamos o objeto Display para decidir o que será exibido na tela. Para fazer isto primeiro temos que pegar o Display atual do MIDlet. Faça a chamada dentro do construtor:
d = Display.getDisplay(this);

Veja que o construtor configura a variável d com o atual Display do MIDlet. Display não foi instanciado, em vez disto usamos o método estático para selecionar o Display atual. Agora dentro de startApp coloque o seguinte código:
Alert mensagem = new Alert("Primeiro MIDlet", "Hello World!", null, AlertType.INFO);
d.setCurrent(mensagem);

Nesse trecho de código criamos um objeto do tipo Alert e depois solicitamos que fosse exibido na tela. O código final ficou assim:
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class HelloWorld extends MIDlet {

Display d;

public HelloWorld() {
d = Display.getDisplay(this);
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
}

protected void pauseApp() {
}

protected void startApp() throws MIDletStateChangeException {
Alert mensagem = new Alert("Primeiro MIDlet", "Hello World!", null, AlertType.INFO);
d.setCurrent(mensagem);
}

}

Para executar clique com o botão direito na classe, Run As > Emulated J2ME MIDlet. O resultado deverá ser algo parecido com isto:


Bom, por hoje é só. Isto foi apenas uma pequena introdução ao J2ME. Espero que apartir daí você possa pesquisar mais sobre o assunto e desenvolver aplicações mais sofisticadas. Eu acredito que qualquer linguagem que aprendemos iniciando no famoso Hello World, temos chances de prosperar. Mas se você tentar aprender alguma linguagem e seu primeiro programa não for o Hello World, certamente irá fracassar. Enfim, isto é apenas uma superstição boba mas eu prefiro não contrariar :)

Bons estudos!!

Garbage Collector - Java

Olá! Hoje vou falar sobre um assunto muito interessante: Coleta de lixo. Uma pergunta muito comum quando falamos de coleta de lixo é "How many objects are eligible for the garbage collector?". Como saber quantos objetos são elegíveis a serem descartados? Vamos descobrir como responder esta questão e também aprender sobre um conceito importante conhecido por Island of Isolation ou Ilha de Isolamento. Antes de continuar vamos ver algo sobre atribuições.


Atribuições

Quando atribuímos um objeto a uma variável, o objeto não é armazenado na variável em si, mas sim no heap. O heap é um espaço na memória onde todos os objetos ficam alocados durante a execução de um programa. As variáveis de instância também residem no heap.
Quando atribuímos um primitivo a uma variável, ela realmente armazena o padrão de bits que representa o valor sendo atribuído a ela, mas se tratando de objetos, a variável armazena o endereço do objeto no heap. Por isto esse tipo de variável que faz referência a objetos é chamada de variável de referência. Abaixo temos um exemplo de código e em seguida uma representação visual:
Object objeto1 = new Object();
Object objeto2 = new Object();
Object objeto3 = new Object();

O resultado disto é:

Garbage Collector

O propósito da coleta de lixo é descartar os objetos que não podem mais ser acessados. Isto é uma forma de gerenciar a memória e evitar que os programas fiquem sem espaço para continuar sua execução.
Quando algum objeto no heap não pode mais ser mais alcançado por ninguém (por nenhum thread ativo), ele é elegível a coleta de lixo. Isto pode acontecer de duas formas:

  1. A variável passa a fazer referência a null:
objeto1 = null;

O resultado disto é:


A outra forma é fazer a variável referenciar outro objeto:
objeto1 = objeto2;

O resultado é:

Qualquer uma das formas tornaria o objeto1 qualificado para coleta.
Suponhamos que o objeto1 esteja "solto" no heap. Posso resolver descartá-lo quando quiser? Não. Somente a JVM pode decidir quando isso deve ser feito, mas como bom programador você pode recomendar que a coleta seja feita. E para fazer isto basta usar a instrução:
System.gc();

Não há garantia de que a coleta seja feita, mas geralmente a JVM atende o pedido.
É importante lembrar que um objeto só existirá no heap caso ele tenha sido inicializado (pela keyword new). Portanto a seguinte declaração não cria nenhum objeto no heap:
Object o = null;

Se o objeto nunca foi criado então não existe objeto a ser coletado.


O Método finalize()

A classe Object possui um método chamado finalize(). Como todas classes são filhas de Object, implícita ou explicitamente, todas herdam tal método. Este método é chamado pelo coletor de lixo quando não há mais referências ao objeto. Mas lembre-se que: se não há garantia que a coleta seja executada quando você solicitar, finalize() pode não ser executado.
O método finalize() de Object não executa nenhuma ação, mas pode ser sobrescrito para que faça algo antes que o objeto seja coletado. Pode ser feito uma liberação de recursos, como fechar um arquivo, finalizar uma conexão, etc. Também podemos usá-lo para salvar o objeto da coleta, daqui a pouco explico melhor.
O método finalize() dá ao programador o poder sobre a vida e a morte dos objetos. Para entender melhor essa afirmação, veja dois conceitos importantes sobre o método:

  • finalize() só é chamado uma única e exclusiva vez para qualquer objeto durante a coleta.
  • Chamar finalize() pode salvar o objeto da coleta.

A primeira afirmação significa que se você chamar finalize() por conta própria, ele não será chamado novamente quando a coleta for executada. E a segunda significa que podemos inserir algum código em finalize() para fazer que o objeto tenha uma nova referência e não seja coletado. Mas ressucitar um objeto não é tão simples assim. Abaixo temos um pequeno exemplo de como isto é feito:
public class Objeto{

// Sobrescreve finalize
public void finalize(){
// Da uma nova referência para objeto1
objeto1 = new Objeto();
}

// Objeto que vai ressuscitar
static Objeto objeto1 = new Objeto();

public static void main(String[] args) {

objeto1 = null; // Elegível a coleta

System.gc(); // Chama finalize como consequência

// Aqui faz o thread dormir um pouco
// enquanto a coleta é feita (espero que seja)
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

objeto1.go(); // Não deveria causar um NullPointerException?

}

void go(){
System.out.println("Estou vivo!");
}
}

Veja que quando main começar a ser executado, objeto1 faz referência a null, se tornando elegível a coleta. Então é solicitado que a coleta seja feita (reze pra JVM aceitar o pedido). Logo após fazemos o thread main descançar um pouco... Há muitas incertezas neste momento. Desejamos que a coleta seja feita e que isso aconteça enquanto main tira um cochilo. Primeiro não sabemos se a JVM vai nos atender e muito menos se isso será feito num segundo. Mas com sorte tudo vai correr bem. Nos meus testes a JVM respondeu bem e a saída foi:
Estou vivo!

Uma parte importante do código é o cochilo de main. Como main e o coletor de lixo rodam em threads separados, não sabemos quem vai tomar o processador primeiro. (mais sobre threads aqui). Pelo que eu percebi, o coletor sempre fica pra depois e isso é compreensível, porque senão ele poderia prejudicar o desempenho do programa. Se o trecho de código onde main fica em espera for removido, antes que o coletor seja executado o método go() será chamado e causará um NullPonterException. Isso mostra que chamar o coletor não garante que ele seja executado naquele exato momento.
Continuando... Agora vamos praticar um pouco. Veja uma questão que tem grandes chances de aparecer no exame SCJP:
Given:
interface Animal {
void makeNoise();
}

class Horse implements Animal {
Long weight = 1200L;

public void makeNoise() {
System.out.println("whinny");
}
}

public class Icelandic extends Horse {
public void makeNoise() {
System.out.println("vinny");
}

public static void main(String[] args) {
Icelandic i1 = new Icelandic();
Icelandic i2 = new Icelandic();
Icelandic i3 = new Icelandic();
i3 = i1;
i1 = i2;
i2 = null;
i3 = i1;
} //Line 14
}
When line 14 is reached, how many objects are
eligible for the garbage collector?

A. 0
B. 1
C. 2
D. 3
E. 4
F. 6

Analisando o código vemos que existem algumas coisas querendo nos desviar do foco principal: Interface e sobrescrição de métodos. Horse implementa o médodo de Animal corretamente e declara uma variável do tipo Long (Long não é long, isso fará diferença no resultado). Depois Icelandic, que é subclasse de Horse, sobrescreve o método makeNoise corretamente. Tirando essas distrações, entramos na execução do código:
Icelandic i1 = new Icelandic();
Icelandic i2 = new Icelandic();
Icelandic i3 = new Icelandic();

A melhor coisa a se fazer é desenhar o esquema:

Note que cada objeto Icelandic possui uma variável weight, herdada de Horse.
Então continuando no programa temos i3 fazendo referência a i1:
i3 = i1;


Neste momento já temos 2 objetos qualificados pra coleta. Porque 2? Lembra da variável Long? Pois então, Long é uma classe wrapper e portanto é um objeto no heap (variáveis de instância residem do heap). Continuando no progama temos:
i1 = i2;


Aqui não temos muitas mudanças, nenhum objeto é qualificado. i1 faz referência ao objeto de i2, mas o objeto que i1 referenciava ainda é acessível através de i3. Continuando:
i2 = null;


Neste momento i2 passou a referenciar null, mas o objeto que era referenciado por ele anteriormente ainda é referenciado por i1. E finalmente temos:
i3 = i1;


Novamente i3 faz referencia a i1, só que agora i1 faz referência ao objeto que era referenciado por i2. Isso torna o objeto (que antes era referenciado por i1 e depois por i3), qualificado para coleta.
Ufa! Mas enfim quantos objetos se tornaram elegíveis a coleta? Quatro. Dois objetos do tipo Icelandic e dois do tipo Long. Geralmente esse tipo de pergunta inclui essas variáveis de instância que são tipos de classe wrapper só pra confudir.
Um modo de saber quantos objetos foram descartados é sobrescrever finalize() para que mostre alguma mensagem. Veja o código:
public class Icelandic {
Long x;

public void finalize() {
System.out.println("tchau");
}

public static void main(String[] args) {
Icelandic i1 = new Icelandic();
Icelandic i2 = new Icelandic();
Icelandic i3 = new Icelandic();

i3 = i1;
i1 = i2;
i2 = null;
i3 = i1;

System.gc();
}
}

A saída deste código é:
tchau
tchau

Quatro objetos foram coletados. Note que a classe tem uma variável de instância Long.


Ilhas de Isolamento

Ilhas de isolamento ocorrem quando variáveis de instância fazem referência umas as outras formando um circuito fechado. Então se as referência a seus referências forem anuladas, os objetos perderão seu vínculo com elas e terão referências apenas uns dos outros. Analise o código:
public class Icelandic {

// Variável de instância
Icelandic i;

public void finalize() {
System.out.println("tchau");
}

public static void main(String[] args) {

Icelandic i1 = new Icelandic();
Icelandic i2 = new Icelandic();

// Variáveis de instância apontam uma para outra
i1.i = i2;
i2.i = i1;

// As variáveis locais perdem a referência
i1 = null;
i2 = null;

System.gc();
}
}

O cenário inicial deste código é representado abaixo:


Depois das declarações a variavel de instância i de i1 (i1.i) faz referência a i2. E a variável de instância i de i2 (i2.i) faz referência a i1:


Logo depois i1 e i2 passam a referenciar null:


Consegue sentir como é dramático este cenário? Embora os objetos tenham variáveis que fazem referência entre eles, nenhum thread ativo pode alcançá-los. Não importa se eles podem acessar um ao outro, de fora ninguém pode acessá-los. Portanto estão qualificados a coleta de lixo.

Conclusão

Sei que esse assunto é um pouco confuso, mas basta fazer os desenhos pra exergar a solução. Bons estudos e até a próxima!


Regras de Cast - Java

Olá! Hoje vamos nos aprofundar um pouco mais nas conversões de tipos. Num outro post eu cheguei a abordar o assunto, porém faltou um esclarecimento maior sobre erros que podem ocorrer. Conversões de tipos no Java podem ser um pouco confusas, pois temos regras para tempo de compilação e de execução. Por exemplo, temos duas classes:
public class Veiculo {

public static void main(String[] args) {

}

}

class Carro extends Veiculo {

}

Um dos casos mais comuns é a conversão ampliadora ou upcasting, onde um tipo mais genérico pode referenciar outro mais específico (um subtipo):
Veiculo v = new Carro();

Temos uma variável de referência do tipo Veiculo que aponta para um objeto do tipo Carro. Sabemos que o contrário não pode ser feito e causa erro de compilação, porque Carro É-UM Veiculo, mas Veiculo NÃO-É-UM Carro.
A conversão ampliadora é totalmente segura, não causará erros de compilação nem execução. E também não exige nenhuma declaração explícita. Simplesmente fazemos a referência e o compilador entenderá que é seguro uma variável do tipo Veiculo referenciar um objeto Carro, pois Veiculo pode fazer coisas que Carro faz. Isso porque Carro herdou estes comportamentos. Na verdade Carro pode fazer ainda mais coisas, porém como Veiculo não sabe dessas coisas, esse tipo de conversão limita os comportamentos de Carro. Agora somente métodos de Veiculo podem ser chamados. Lembre-se que a variável de referência é quem diz quais métodos poderão ser chamados.

Vamos continuar e ver uma conversão redutora, ou downcasting:
Carro c = v;

Veja que estamos indo pra baixo na árvore de herança agora. Tentamos fazer com que a variável de refência do tipo Veiculo (usada anteriomente para referenciar Carro), seja atribuída a outra variável do tipo Carro, fazendo com que as duas apontem para o mesmo objeto. Um pouco confuso mas veja como funciona:

Antes fizemos isto:

Veiculo -> Carro


Agora estamos tentando isto:

Carro = Veiculo -> Carro

Essa conversão não pode ser feita implicitamente, como no upcasting. O código anterior não compila, mas como sabemos que a variável v aponta pra um objeto do tipo Carro, podemos fazer a conversão explicitamente:

Carro = (Carro)Veiculo -> Carro

Isto é:
Carro c = (Carro)v;

Isto funcionou porque v faz referência a um objeto Carro. Então é seguro a referência Veiculo para Carro. Lembre-se que o tipo de objeto sendo refenciado é que determina se a conversão poderá ser feita. Mas e se o objeto referenciado não fosse Carro? Agora fica interessante porque o compilador lava suas mãos. Portanto, o seguinte código vai compilar:
Veiculo v1 = new Veiculo(); //Objeto Veiculo

Carro c1 = (Carro)v1;

Mas como o que estamos tentando fazer é isto:

Carro = (Carro)Veiculo -> Veiculo!!


Quando executamos o código, a seguinte mensagem será exibida:

Exception in thread "main" java.lang.ClassCastException:
Veiculo cannot be cast to Carro

Outros códigos serão executados normalmente, mas quando tentar a conversão acima, a exceção vai finalizar o programa. Isso nos mostra que só podemos fazer downcasting se o objeto sendo refenciado for do tipo da variável que vai fazer a referência. O compilador não sabe se estamos fazendo isso errado, mas podemos nos prevenir de duas formas. Uma é usando o operador instanceof:
Veiculo v1 = new Veiculo();

if (v1 instanceof Carro) {
    Carro c1 = (Carro) v1;
}else
    System.out.println("Não foi possível...");

Outra é tratando a exceção:
try{
    Carro c1 = (Carro)v1;
}catch (ClassCastException e) {
    System.out.println("Não foi possível...");
}

Com isso espero ter esclarecido dúvidas sobre erros de compilação e execução causados pelas conversões.
Por hoje é só pessoal! Bons estudos..

Sobrescrita e Exceções

Introdução

Hoje vou escrever sobre um assunto que está presente no objetivo 1.5 da SCJP. O objetivo determina que o candidato saiba identificar se a sobrescrição de um método está correta. Eu já falei sobre sobrescrita num outro post, mas fiquei devendo uma explicação melhor sobre exceções.


Exceções Verificadas (checked)

As exceções verificadas são exceções que devem ser tratadas por você no seu código. O compilador irá forçá-lo a lidar adequadamente com essas exceções. Você pode capturar essas exceções ou declará-las na cláusula throws do método.
Geralmente, as exceções verificadas ocorrem devido a condições fora de seu controle, como IOException ou uma falha na conexão de rede. Mas sua aplicação deve prever essas condições e tratá-las corretamente para que não venha travar. É por isso que você é forçado pelo compilador a lidar com essas exceções. Se a conexão de rede é interrompida durante uma transferência de arquivo, o aplicativo deve lidar com isso e permitir que o usuário tente novamente.


Exceções Não verificadas (unchecked)

Por outro lado, exceções não-verificadas ocorrem devido a erros de codificação por parte do programador. Como uma exceção NullPointerException que pode ser evitada através de uma verificação para saber se a referência é nula.
Existem também algumas exceções unchecked que podem ocorrer que não são culpa do programador. Estas são as exceções que são subclasses da classe java.lang.Error. Quando essas exceções ocorrem, não há muito o que fazer, então você não é obrigado a lidar com elas já que não pode prever quando irão ocorrer. Como um OutOfMemoryError pode ocorrer a qualquer momento que um objeto é instanciado, você não pode colocar cada instanciação de objeto em blocos try-catch.

Exemplos

Vamos criar um exemplo para ver como o compilador nos força a tratar exceções verificadas.
class ExcecaoNaoTratada {
public static void main(String[] args) {
 throw new Exception();
}
}

O código acima não vai compilar porque não estamos tratando a exceção verificada. Para tornar o código compilável podemos colocar a instrução num bloco try-catch afim de capturá-la ou declarar a exceção numa cláusula throws do método main que irá lançá-la.

No caso de exceções não verificadas, se o método lançar uma exceção não verificada, o compilador não vai reclamar se ela não for tratada. Então o seguinte código compila normalmente:
class ExcecaoNaoTratada {
public static void main(String[] args) {
 throw new RuntimeException();
}
}


Hierarquia de classes

A classe java.lang.Throwable é uma exceção verificada. A API Java define duas subclasses de Throwable. Estes são java.lang.Exception e java.lang.Error. Error é uma classe de exceção não verificada, enquanto Exception é uma exceção verificada. A classe Exception em si tem a subclasse java.lang.RuntimeException (não verificada). As exceções não verificadas como NullPointerException, ClassCastException etc, são todas as sub-classes de RuntimeException. Todas as subclasses de RuntimeException e Error são exceções não verificadas. Outras classes são exceções verificadas.


Se você criar sua própria classe de exceção, se ela é verificada ou não, depende de qual classe Exception ela é subclasse. Se a sua classe estende uma classe de exceção verificada, então sua classe será uma exceção verificada. E se a sua classe estende uma classe de exceção não verificada, então a sua classe será uma exceção não verificada.

Existe uma regra com exceções verificadas que às vezes confunde as pessoas. Se no seu bloco catch você está capturando uma exceção verificada, então deve haver a possibilidade de sua ocorrência no bloco try. Assim, o código a seguir não vai compilar:
try {
System.out.println("...");
} catch(java.io.IOException ioe) {
}

O compilador irá informar que IOException nunca é lançada no bloco try, então você não pode capturá-la. Esta regra não se aplica às exceções não verificadas. Se você está capturando uma exceção não verificada, que não é lançada no bloco try, o compilador não vai reclamar. Agora fica um pouco confuso com as classes Exception e Throwable. Ambas são classes de exceções verificadas, mas você pode capturá-las sem qualquer exceção sendo lançada no bloco try. Então, esse código será compilado sem erros:
try {
System.out.println("...");
} catch(Exception ex) {
}

As regras são diferentes para Throwable e Exception e outras classes de exceção verificada. Isto porque tanto Exception e Throwable tem exceções não verificadas como subclasses. Assim, o compilador permite capturá-los pensando que você pode estar tentando pegar uma exceção não verificada. Lembre-se que o compilador não está preocupado com as exceções não verificadas. RuntimeException é uma subclasse de Exception e Error é uma subclasse de Throwable, e ambos RuntimeException e Error são classes de exceção não verificada. Então, quando escrever o código:
try {
System.out.println ("...");
} Catch (Exception ex) {
}

O compilador não vai reclamar porque Exception também pode capturar exceções não verificadas (RuntimeException e suas subclasses). Da mesma forma o código a seguir é permitido porque Throwable pode capturar exceções não verificadas (RuntimeException e Error e suas subclasses).
try {
System.out.println ("...");
} Catch (Throwable ex) {
}


Regras

Antes de continuar quero adiantar que os tópicos anteriores foram traduzidos deste site. Na verdade isso fugiu um pouco ao objetivo do exame mas serve como um esclarecimento sobre exceções. Agora vejamos ao que realmente importa no exame:
  1. O método novo não pode lançar exceções verificadas novas ou mais abrangentes;
  2. O método novo pode lançar exceções verificadas mais restritivas ou menos abrangentes;
  3. O método novo pode lançar qualquer exceção não verificada;
A maioria dos programadores Java utilizam alguma IDE para desenvolver. É muito mais produtivo. As vezes utilizamos Notepad só para fazer alguns testes, mas normalmente alguém em boas condições mentais não programaria no bloco de notas. As IDE´s quase sempre sabem quando estamos fazendo algo errado. Aí então damos um clique e está resolvido. As regras acima são exemplos de coisas que podemos deixar por conta da IDE, mas no exame não! Para o exame você deve ter um compilador rodando em seu cérebro. A cada código analisado, você deverá saber se está certo ou não. Então vamos treinar um pouco pra isso.
A primeira regra diz que os métodos novos não podem lançar excessões novas ou mais abrangentes (menos restritivas). Abaixo temos um código que será usado como exemplo, nele temos uma sobrescrição simples:
public class Excecoes extends Exemplo{

void A (){
// Sobrescreveu..
}

}

class Exemplo{

void A (){

}

}

A primeira coisa é que não se pode fazer isso:
void A () throws Exception {
// Sobrescreveu..
}

O método novo tentou lançar uma exceção verificada nova. O código não compila. Agora analise este caso:
import java.io.IOException;

public class Excecoes extends Exemplo{

void A () throws Exception { //Tentou lançar uma exeção mais abrangente...
// Sobrescreveu..
}

}

class Exemplo{

void A () throws IOException{

}

}

O código acima também não compila. O método novo tentou lançar uma exceção mais abrangente. Com isso vimos como funciona a primeira regra. Vamos agora pra segunda:
import java.io.IOException;

public class Excecoes extends Exemplo{

void A () throws IOException { //Excesão menos abrangente e mais restritiva
// Sobrescreveu..
}

}

class Exemplo{

void A () throws Exception{

}

}

O código acima compila. O novo método lançou uma exceção que é subclasse de Exception, por isso ela é menos abrangente. O método sobrescrito se arrisca a lançar qualquer tipo de Exception, mas o método novo não precisa declarar nada que nunca lançará.
E a terceira e última regra diz que o método novo pode lançar qualquer exceção não verificada. Isso significa que o seguinte código é válido:
public class Excecoes extends Exemplo{

void A () throws RuntimeException {
// Sobrescreveu..
}

}

class Exemplo{

void A (){

}

}

Outro detalhe importante também é que o método novo pode resolver não lançar nenhuma exceção. Portanto, se ver um método sendo sobrescrito e não lançar nenhuma exceção (mesmo que o método da superclasse o faça) saiba que isto é válido.

Conclusão

Espero ter deixado mais claro este assunto que é muito importante não só pra quem vai tirar uma certificação mas também pra quem está programando no dia a dia e sabe que o negócio não é só ficar lançando exceção ou capturando sem nenhuma codificação no bloco Catch.

Até a próxima!

Inserindo labels dinamicamente

Olá! Caro leitor. Hoje pela manhã estive olhando uns fóruns sobre Java quando me deparei com a seguinte pergunta: Como posso inserir labels dinamicamente em um JPanel? Então eu com meu espírito de colaborador, desenvolvi um pequeno código para responder a pergunta do indivíduo. Este código pode ser encontrado aqui.
Mas o que me inspirou mesmo fazer esta postagem não foi exatamente isto. Foi que me veio a cabeça modificar esse código e fazer com que ele mostrasse miniaturas de imagens, isto é, thumbnails. Sabemos que JLabels podem ser usados para exibir imagens num aplicativo, então bastava buscar as imagens num diretório e mostrá-las na tela. Portanto é isso que eu pretendo mostrar agora.
Primeiramente vamos ver o código básico para mostrar uma janela. Estou utilizando Swing pra fazer isso:

import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;


public class LabelDinamico {

public static void main(String[] args) {

JFrame janela = new JFrame("Inserindo labels dinamicamente");
janela.setBounds(0, 0, 500, 500);
janela.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
janela.setLayout(null);

//Códigos legais aqui...

janela.setVisible(true);
}
}

Não temos nada demais aí. Alguns imports, uma classe com o método main, instanciamos e mostramos uma janela. Agora vamos aos códigos legais! Duas coisas básicas antes de inserir os labels:

// Diretório contendo as imagens
File diretorio = new File("C:\\imagens");
File[] arquivos = diretorio.listFiles();

// Posicionamento dos labels
int boundX = 10;
int boundY = 10;

Primeiro pegamos o caminho do diretório que contém as imagens a serem mostradas. Se esse diretório tiver outro tipo de arquivo que não seja imagem, serão mostrados espaços vazios. Seria interessante um filtro para mostrar somente imagens, mas isso fica pra depois. Então um vetor de arquivos contém a lista das imagens. Logo em seguida declaramos variáveis que vão auxiliar no posicionamento dos labels. Isso porque os labels deverão ficar em colunas e quando chegar na largura da janela, outra fila deverá ser formada. Você vai entender melhor adiante.
Então finalmente teremos um pouco de ação! Veja como percorrer o vetor de arquivos e inserir as imagens na janela:

// Adicionando labels dinamicamente
for (int x = 0; x /* sinal de menor aqui */ arquivos.length; x++){

// Declara uma imagem com o arquivo na posição do vetor
final ImageIcon imagem = new ImageIcon(((File)arquivos[x]).getAbsolutePath());

// Declara o label
JLabel lbImagem = new JLabel()
// Esse bloco é para inserir a imagem
// e esticar de acordo com o tamanho label
{
public void paintComponent (Graphics g) {
super.paintComponent (g);
// Esses 5 sendo subtraídos é pra dar um espaço entre as imagens
g.drawImage (imagem.getImage(), 0, 0, getWidth() - 5, getHeight() - 5, null);
}
};

// Posiciona a imagem
lbImagem.setBounds(boundX, boundY, 70, 70);

// Seta o nome com o caminho da imagem
// Será usado depois para retornar o caminho quando clidado
lbImagem.setName(((File)arquivos[x]).getAbsolutePath());

// Adiciona um listener
lbImagem.addMouseListener(new MouseListener() {
public void mouseReleased(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mouseEntered(MouseEvent arg0) {}
public void mouseClicked(MouseEvent arg0) {

// Pega o label que foi clicado
JLabel lb = (JLabel) arg0.getSource();
// Mosta o caminho da imagem clicada
JOptionPane.showMessageDialog(null, "O caminho da imagem é " + lb.getName(), "Imagem", JOptionPane.INFORMATION_MESSAGE);
}
});

// Prepara a posição x do próximo label
boundX += 70;

// Verifica se estourou o limite
if (boundX /* sinal de maior ou igual aqui */ janela.getWidth() - 70)
{
// Desce uma fila
boundY += 70;
boundX = 10;
}

//Adiciona o label
janela.add(lbImagem);
}

O código está bem comentado, mas ainda assim vou fazer mais algumas considerações:
  • Note que primeiramente percorremos o vetor dos arquivos utilizando um for (tive que retirar o sinal de menor pq o blogger não entende isso...)
  • Então inicialmente criamos um ImageIcon que contém a imagem do arquivo que está no vetor daquela posição.
  • Logo depois declaramos o label e utilizamos paintComponent para desenhar a imagem no label. Isto é usado para que a imagem fique do tamanho do label. A imagem será esticada (pra mais ou pra menos), para caber no label.
  • Em seguida é feito o posicionamento e a propriedade name é configurada com o caminho do arquivo. Isso é porque depois usaremos esta mesma propriedade para retornar o caminho quando a imagem for clicada. De outra forma não teria como saber qual imagem está desenhada no label. Poderíamos criar uma subclasse de label e adicionar uma ou mais propriedades adequadamente, mas deixaremos assim mesmo por enquanto.
  • Um listener é criado para mostrar a propriedade configurada anteriormente quando a imagem for clicada. Poderemos desejar fazer alguma coisa com isto depois.
  • Finalmente a posição é configurada, levando em conta o tamanho da janela. O label é adicionado na janela.
Vejamos como ficou:

Outra coisa que podemos fazer é pegar todos os componentes da janela (no caso, os labels) e exibir suas propriedades ou modificar alguma coisa. Veja como fazer isto:

for (int x = 0; x /* sinal de menor aqui */ labels.length; x++){
if (labels[x] instanceof JLabel)
{
// Mostra todos o name dos labels
System.out.println(((JLabel)labels[x]).getName());
}
}

Bom, é isso que eu tinha pra passar pra vocês hoje. O código completo você encontra aqui. Sei que falta muita coisa ainda pra que esse código se torne um componente de verdade e possa ser reutilizado mas o primeiro passo está dado. Espero que tenham gostado e até a próxima!

Introdução ao JavaScript - Parte 2

Depois de algum tempo sem postar aqui, devido a projetos pessoais e faculdade, estou de volta para mostrar mais algumas coisas sobre JavaScript como havia prometido. Então vamos lá!


O Objeto document

Document é um objeto usado para modificar ou obter propriedades da página que está sendo exibida. Ele é um container com todos objetos que estão na página HTML.
Vamos começar com um simples exemplo de como esse objeto pode ser usado:



Neste exemplo usamos o método write e a propriedade location de document para escrever na página e mostrar qual a atual URL sendo exibida.
Outro caso simples de uso seria para alterar a cor de fundo da página:



Obviamente o objeto document possui muitos outros métodos e propriedades, mas não vou abordar todos aqui, caso queira saber mais entre aqui.


Outros objetos

Existem outros objetos que também são importantes no JavaScript:
  • Window
  • Navigator
  • Screen
  • History
  • Location
Uma propriedade do objeto navigator muito utilizada serve para retornar o navegador utilizado:



Aqui segue uma boa referência dos objetos usados no JavaScript.


Semelhanças entre Java e JavaScript

Esse tópico não é sobre características nem finalidades das linguagens, mas sim sobre semelhanças em sua sintaxe. Vamos começar com os operadores.
Os operadores no Java são praticamente os mesmos do JavaScript:
  • Aritméticos: +, -, *, /, %, ++, --
  • Atribuição: +=, -=, *=, /=, %=
  • Comparação: ==, !=, >, >=, <, <=
  • Lógicos: &&, ||, !
JavaScript não suporta o operador instanceof. Outra coisa importante sobre operadores é que strings devem ser comparadas utilizando ==, veja:



O resultado é true. Porém se uma string for inicializada assim:

var x2 = new String("teste");
var x3 = new String("teste");

A comparação deve ser feita deste modo:

if(x2.toString() == x3.toString())
document.write("true");
else
document.write("false");

Achei essa dica aqui...
Com isso também podemos ver que a estrutura de comparação if é a mesma nas duas linguagens.

Agora vamos para a estrutura de seleção switch:

switch (variável)
{
case 'valor 1':
{
//bloco de comandos;
break;
}
case 'valor2':
{
//bloco de comandos;
break;
}
default:
{
//comando alternativo;
}
}

Aqui temos uma estrutura básica. Temos duas diferenças da linguagem Java. Uma são as chaves entre os blocos e outra é o tipo de dados. No Java o switch trabalha com os tipos primitivos byte, short, char, e int (existem algumas exceções mas basicamente é isso). Já no JavaScript podemos trabalhar com strings também. Continuando o exemplo anterior:

switch (x1)
{
case 'texto':
{
document.write("Ok!");
break;
}
case 'valor':
{
//bloco de comandos;
break;
}
default:
{
document.write("Nenhum!");
}
}

Veja também que tanto faz usar aspas simples ou duplas. Outra coisa é que os comentários podem ser feitos da mesma forma que o Java (//... ou /* ... */ ).

Agora vamos para estruturas de repetição. No JavaScript temos 3 tipos delas, for, while e do while. Alguns dizem que são 4 estruturas, contando que existem 2 tipos de for (for normal e for in). Exceto o for in, as outras funcionam da mesma forma nas duas linguagens:



Assim como no Java, o JavaScript também aceita as instruções break e continue.
Agora o for in. O for in funciona como o for each do Java. Temos um array com elementos e usamos o for in para percorrê-lo:

var x;
var carros = new Array();
carros[0] = "Ferrari";
carros[1] = "Lamborghini";
carros[2] = "Porsche";

for (x in carros)
{
document.write(carros[x] + "
");
}

Conclusão

Finalizo essa introdução ao JavaScript e espero que possam ter tirado algum proveito das informações contidas aqui. Sei que é pouca coisa mas dá pra começar. Tem um site muito bom que tenho usado como referência: w3schools. Esse site tem muito conteúdo, não so de JavaScript mas outras linguagens também.

Bons estudos!

Introdução ao JavaScript - Parte 1

Hoje teremos um post sobre JavaScript pra variar um pouco. Farei este post em duas partes. Na primeira vou fazer uma introdução sobre a linguagem dando alguns conteitos básicos e também falarei sobre variáveis e eventos. Na próxima parte teremos objetos importantes, suas propriedades e também sobre semelhanças com a linguagem Java.

O que é JavaScript?

De acordo com a famosa enciclopédia digital "JavaScript é uma linguagem de programação criada por Brendan Eich para a Netscape em 1995, que a princípio se chamava LiveScript. A Netscape, após o sucesso inicial desta linguagem, recebeu uma colaboração considerável da Sun, a qual possibilitou posteriormente dizer que o JavaScript é uma linguagem compatível com a linguagem Java, por esta razão, a semelhança dos nomes."
Pontos importantes sobre JavaScript quanto aos tipos:
  • fraca - sua tipagem é mutável;
  • dinâmica - uma variável pode assumir vários tipos diferentes durante a execução;
  • implícita - as variáveis são declaradas sem tipo.
Porque usar JavaScript? JavaScript permite tornar páginas HTML mais dinâmicas. Usamos a linguagem para interagir com a página, validar formulários, etc. Vamos ao que interessa.

10 Perguntas básicas sobre JavaScript

Estas perguntas foram baseadas num quiz disponível aqui. É necessário obter 100% pra dizer que se tem um conhecimento básico sobre JavaScript.

1. Qual elemento é utilizado para os scripts?

Em uma página HTML colocamos os códigos JavaScript dentro de tags script, como mostra o exemplo:



2. Qual é o lugar correto para inserir JavaScript?

Os scripts podem ser inseridos tanto no cabeçalho(head) quanto no corpo(body) da página.

3. Qual a sintaxe para fazer referência a um script externo?

Scripts externos devem ser salvos com a extenção js e podem ser referenciados utilizando o parâmetro src:



4. Arquivos JavaScript externos precisam ter a tag script?

Não, somente códigos.

5. Como escrever um texto na página?

Para escrever na página usa-se document.write:

document.write("Hello World");

6. Como mostrar mensagens utilizando uma caixa de alerta?

Mensagens podem ser exibidas numa caixa de alerta simplesmente chamando:

alert("Hello World");

7. Como criar uma função?

Funções podem ser criadas utilizando a palavra function, veja:

function myFunction()
{
//Códigos da função
}

8. Como chamar uma função?

Funções podem ser chamadas simplesmente digitando seu nome:

myFunction()

9. Como abrir uma janela?

Pode-se abrir uma janela chamando window.open:

window.open("http://marciosouzajunior.blogspot.com");

10. Como colocar uma mensagem de status no navegador?

Mensagens de status são uma propriedade do objeto window e podem ser modificadas diretamente:

window.status = "Mensagem";

Obviamente existe muito mais sobre JavaScript que você deveria saber. Essas perguntinhas foram apenas pra você poder falar com os amigos que sabe alguma coisa de JavaScript. E também se algum dia for fazer uma prova que caia JavaScript, não passe em branco. Vamos ver outras coisas importantes agora.


Variáveis

JavaScript possui 3 tipos de dados: número, caractere e booleano. Não tem segredo. Como já foi dito, as variáveis podem ser declaradas sem um tipo específico. A simples associação de um valor faz com que a variável passe a existir:

x = 2;

E seu tipo pode mudar como quiser:

var x = 2;
alert(x);
x = "texto";
alert(x);

Usar a palavra var é opcional. Usamos apenas quando queremos declarar a variável sem um valor inicial. Se fizermos isto o seu valor será undefined. Também podemos associar um valor null, que é quase a mesma coisa que undefined.
Não tem muito o que falar sobre variáveis em JavaScript, os criadores da linguagem deixaram as coisas bem simples de trabalhar.

Eventos

Eventos são coisas que podem acontecer durante a execução de um programa (neste caso da página). Um clique de mouse, uma caixa que perde o foco, são eventos que podem ocorrer a qualquer momento. Podemos desejar que esses eventos disparem alguma ação. Logo abaixo temos uma lista de eventos que o JavaScript pode capturar e quando eles acontecem:

  • onLoad: Quando a página é carregada.
  • onUnload: Quando a página é fechada.
Esses dois eventos só ocorrem no body, portanto poderiam chamar alguma ação da seguinte forma:

<_body onload="alert('Oi');">

Todos sabemos que body não tem underline, mas sou obrigado a escrever assim porque mais uma vez o blogger está confundindo as coisas. Espero que todos tenham entendido. Continuando...Agora vamos ver 3 eventos que podem ocorrer em objetos text, select e textarea:
  • onChange: Acontece quando o objeto perde o foco e houve mudança de conteúdo.
  • onBlur: Acontece quando o objeto perde o foco, independente de ter mudado o conteúdo.
  • onFocus: Quando o objeto ganha o foco.
Aqui temos um exemplo:



O próximo evento pode ser usado para text e textarea:
  • onSelect: Ocorre quando seleciona parte ou todo texto do objeto.
Os próximos eventos tem a ver com o mouse:
  • onClick: Quando o objeto é clicado. Pode acontecer nos objetos button, checkbox, radio, link, reset e submit.
  • onMouseOver: Quando passa o mouse sobre o objeto.
  • onSubmit: Quando clica no botão submit. Váido somente para forms.
Exemplo:

Blog do Márcio

Certamente numa prova de JavaScript teria pelo menos uma questão sobre esses eventos. Principalmente sobre o evento onBlur, porque facilmente alguém acreditaria que um suposto evento onLostFocus estaria certo.

Fim da Parte 1

Bom, ficamos por aqui com esta introdução ao JavaScript. Espero que você possa ter tirado algum proveito do material. Em breve teremos a segunda parte com mais informações sobre os objetos document, window e também sobre semelhanças com a o Java.


Bons estudos!

Enums - Java

Enums são um tipo de classe Java que quase nunca utilizamos. Acredito que muita gente nem imagina que esse tipo de classe existe. Diferente de classes comuns e interfaces, o Enum foi inserido na versão 5 do Java. Ele permitie enumerar uma quantidade de valores pré-definidos que podem ser aceitos para uma variável.
Vamos a um exemplo prático. Precisamos de uma variável que só aceite 3 valores: maçã, pera e uva. Então declaramos:
enum Frutas { MAÇÃ, PERA, UVA }; //É opcional colocar ponto-e-vírgula
Obs.: Por convenção as constantes são escritas em maiúsculas. Um Enum é constituido basicamente por suas constantes, mas logo veremos que podem existir outras coisas a mais. Eles podem ser declarados como membro de uma classe ou fora da classe, mas nunca dentro de métodos (inclusive o main). Outro ponto importante é que os Enums só aceitam os modificadores default e public (não aceita private, protected, final, abstract nem static). Veja um exemplo de declaração fora da classe:
enum Frutas { MAÇÃ, PERA, UVA }

public class Run {

 Frutas tipo; //Run tem uma variável de instância do tipo Frutas

 public static void main(String[] args) {
 
     Run r = new Run();
     r.tipo = Frutas.MAÇÃ;
 
 }
}
Enums não são do tipo String ou de outro tipo. Eles são do seu próprio tipo, neste caso Frutas. MAÇÃ, PERA ou UVA são tipos de Frutas. Se fizermos o teste:
if (Frutas.MAÇÃ instanceof Frutas)
 System.out.println("Maçã é do tipo Fruta");
O resultado será: Maçã é do tipo Fruta
Então sabemos o que são Enums, como e onde declará-los. E também sabemos que por ser uma classe, tem seu próprio tipo. Agora vamos ver umas coisas mais interessantes.
Construtores, Métodos e Variáveis em um Enum
Como os Enums são um tipo especial de classe, eles podem ter construtores, variáveis e até um corpo de classe para as constantes (mais adiante explico o que é essa coisa bizarra). Vamos ver como funcionam esses membros passando um parâmetro para cada constante:
 MAÇÃ("vermelha"), PERA("verde"), UVA("roxa");
Cada tipo de fruta agora recebe um parâmetro do tipo String, como numa chamada de método normal (poderíamos ter outros tipos, dependendo do construtor). De acordo com os argumentos, neste caso teríamos o seguinte construtor:
 Frutas(String cor){
     this.Cor = cor;
 }
Sendo assim, devemos ter uma variável de instância para receber a cor:
 private String Cor;
E ainda um método para retornar a cor da fruta escolhida:
 void getCor(){
     System.out.println("A cor da fruta escolhida é " + Cor);
 }
O Enum completo ficaria assim:
enum Frutas {

 //Passando parâmetros a cada constante
 MAÇÃ("vermelha"), PERA("verde"), UVA("roxa");

 //Construtor
 Frutas(String cor){
     this.Cor = cor;
 }
 //Variavel de instância
 private String Cor;

 //Método que mostra a cor da fruta escolhida
 void getCor(){
     System.out.println("A fruta escolhida é " + Cor);
 }

}
Observe que quando temos mais membros além das constantes é obrigatório o ponto-e-vírgula após as constantes.
Agora modifique a classe Run para que fique parecida com esta:
public class Run {

 Frutas nome;

 public static void main(String[] args) {
 
     Run r = new Run();
     r.nome = Frutas.MAÇÃ;
     r.nome.getCor();
 }
}
A saída será: A fruta escolhida é vermelha
Sobre o construtor do Enum devemos saber que ele nunca poderá ser invocado diretamente. Ele só pode ser chamado pelo próprio Enum, ou melhor, por cada constante. Os construtores também podem ser sobrecarregados, como no exemplo:
 MAÇÃ("vermelha"), PERA("verde"), UVA("roxa", "verde");

 Frutas(String cor){
     this.Cor = cor;
 }
 //Construtor sobrecarregado
 Frutas(String cor, String cor2){
 }
Corpo de Classe específico da constante
Como eu mencionei anteriormente, os Enums podem possuir uma coisa estranha chamada corpo de classe específico da constante. Esse corpo é um bloco de código que podemos definir para cada constante. Usamos esse corpo para substituir um método definido dentro do Enum. Vamos ver na prática:
enum Frutas {

 MAÇÃ,
 PERA,
 UVA{
     //Corpo da constante UVA
     //Códigos legais aqui...
 };

 void getFoo(){
     System.out.println("Frutas são saudáveis.");
 }

}
Observe que se chamarmos por r.nome.getFoo(), independente de qual fruta tenha sido escolhida, sempre teremos a saída: Frutas são saudáveis. Porém agora vamos sobrescrever o método dentro do corpo da constante UVA:
enum Frutas {

 MAÇÃ,
 PERA,
 UVA{
     void getFoo(){
         System.out.println("BAD FRUIT!!!");
     }
 };

 void getFoo(){
     System.out.println("Frutas são saudáveis.");
 }

}
E então chamamos:
     r.nome = Frutas.UVA;
     r.nome.getFoo();
A saída será: BAD FRUIT!!!
Ok, mas pra que fazer isso? Imagine que todos os elementos do Enum tenham uma propriedade, um código por exemplo. Mas um determinado elemento não pode ter aquele código. Então usamos o corpo da constante para definir um comportamento especial pra ele. É realmente difícil imaginar uma aplicação para isso, mas apenas saiba como funciona que será o bastante para passar no exame! E por hoje é só.
Bons estudos!

Menu Dinâmico - Java

Olá! Hoje vou mostrar como implementar um menu dinâmico. Na verdade eu usei esta aplicação para gerar um menu com todos os LookAndFeels disponíveis. Criar menus ou qualquer outro tipo de objeto dinâmico significa que ele é criado em tempo de execução. Mas pra que fazer isso? Por exemplo, num sistema ERP cada usuário tem um nível de acesso, então dependendo de quem logar no sistema, determinados itens no menu estarão disponíveis ou não. O sistema verifica no banco de dados quais as permissões daquele usuário e monta o menu em tempo de execução.
Mas no exemplo que vou mostrar não usa banco de dados, mas seria interessante implementar alguma forma de salvar o LookAndFeel selecionado. Porque fazer isto dinamicamente? Neste caso o menu teria que ser dinâmico porque os LookAndFeels podem ser instalados ou removidos da JRE.
Também vou mostrar como escrever um ActionListener que será usado ao acionar os eventos dos itens no menu.

Utilizando LookAndFeels

Antes de mais nada vamos fazer os seguintes imports:

import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;

E também declarar uma variável de instância:

static LookAndFeelInfo skins[] = UIManager.getInstalledLookAndFeels();

Agora temos um array com os LookAndFeels disponíveis. Deixaremos como static para não ter que instanciar depois quando for preciso chamar no Listener (mais adiante você vai entender).

Montando o Menu

Para montar o menu eu criei um método que retorna um tipo JMenuBar. Para adicionar o menu na sua aplicação simplesmente chame o método dentro do construtor:

super.setJMenuBar(montaMenu());

O método é algo parecido com isto:

JMenuBar montaMenu(){

//Cria a barra
JMenuBar barraMenu = new JMenuBar();

//Cria o menu
JMenu menuSkins = new JMenu("Skins");

/*Aqui vamos adicionar os itens dinamicamente no menu Skins
Mas por enquanto um pouco de suspense....*/

//Adiciona o menu na barra
barraMenu.add(menuSkins);

//Retornamos a barra prontinha
return barraMenu;

}

Bom, agora só falta adicionar os itens no menu Skins. Esta parte pode parecer um pouco confusa a primeira vista mas não é!
Primeiramente vamos criar um array do tipo JMenuItem do tamanho de skins.length, isto é, com quantas posições forem necessárias para acomodar todos os LookAndFeels instalados.

JMenuItem itemSkin[] = new JMenuItem[skins.length];

Agora criamos outro array do tipo myListener (???) para os ActionListeners dos itens declarados acima. Calma que logo veremos essa classe myListener. O tamanho do array segue o mesmo exemplo mostrado.

myListener [] eventosMenu = new myListener[skins.length];

Agora vai ficar bom! Vamos entrar num for passando por todos skins:

for (int skin = 0; skin /*sinal de menor aqui*/ skins.length; skin++)
{
itemSkin[skin] = new JMenuItem(skins[skin].getName());
eventosMenu[skin] = new myListener(skin);
itemSkin[skin].addActionListener(eventosMenu[skin]);
menuSkins.add(itemSkin[skin]);
}

O que acontece? Primeiro o array itemSkin recebe um item com o nome do LookAndFeel. Então um Listener é criado no array e recebe como parâmetro o número daquele skin (mais adiante explico porque). Depois o ActionListener é adicionado ao item. Por último o item é adicionado ao menu. Nao esqueça de colocar o sinal de menor no for onde está comentado, se eu colocar o Blogger pensa que é uma tag e atrapalha tudo! Se alguém souber como desativar essa verificação me avise!
O método deverá ficar assim:


JMenuBar montaMenu(){

//Cria a barra
JMenuBar barraMenu = new JMenuBar();

//Cria o menu
JMenu menuSkins = new JMenu("Skins");

for (int skin = 0; skin /*sinal de menor aqui*/ skins.length; skin++)
{
itemSkin[skin] = new JMenuItem(skins[skin].getName());
eventosMenu[skin] = new myListener(skin);
itemSkin[skin].addActionListener(eventosMenu[skin]);
menuSkins.add(itemSkin[skin]);
}

//Adiciona o menu na barra
barraMenu.add(menuSkins);

//Retornamos a barra prontinha
return barraMenu;

}

Escrevendo um Listener

Para acionar os eventos do menu, o autor achou que seria interessante implementar um Listener personalizado. Isto porque o Listener deveria conter o número do LookAndFeel que ele gostaria de mostrar naquele momento. Para escrever um Listener, implemente a classe ActionListener e o método actionPerformed:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import javax.swing.UIManager;

public class myListener implements ActionListener{
private int skin = 0;

public myListener(int newSkin) {
this.skin = newSkin;
}

public void actionPerformed(ActionEvent arg0) {
try{
UIManager.setLookAndFeel(Principal.skins[skin].getClassName());
JOptionPane.showMessageDialog(null, "Seu skin foi alterado para " + Principal.skins[skin].getName() + ".\nCaso tenha janelas internas em execução, reinicie-as para aplicar as alterações.", "Skins", JOptionPane.INFORMATION_MESSAGE);
}catch(Exception e)
{
e.printStackTrace();
}
}
}

Veja que a classe possui um construtor que recebe o LookAndFeel a ser mostrado. Por isso que passamos como parâmetro quando estamos criando os objetos Listener. Dentro do actionPerformed chamamos o método que configura o LookAndFeel recebido e mostra uma mensagem. Coloquei esta mensagem porque estou trabalhando com InternalFrames e eles precisam ser reabertos para que as aplicações ocorram. Lembrando que as duas classes estão no mesmo pacote.
Veja o resultado final:


Agora é só implementar mais alguma coisa pra salvar o LookAndFeel selecionado.
Espero que tenham gostado!