Java com café: outubro 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!