Java

Friday, May 12, 2006

 

Tokens

Java nos fornece pelo menos três maneiras distintas de lidar com tokens. A classe String contém o método split(), por meio do qual dividimos um objeto String em tokens. Por default, o método split() usa o espaço em branco como delimitador, mas podemos passar como argumento qualquer expressão regex.

String s = "bla bla bla bla";
String token [] = s.split();

token receberá um vetor de tamanho quatro, contendo os tokens bla, bla, bla e bla.

String s = "a3 5aab5haf"
String token [] = s.split("\\d");

Agora usamos um dígito como delimitador. O resultado será também um vetor de tamanho quatro, contendo os tokens "a", " ", "aab", "haf".

A classe StringTokenizer nos fornece outra maneira de obter os tokens a partir de um objeto String. Ambas as construções abaixo são possíveis:

String s = "a3 5aab5haf"
StringTokenizer st = new StringTokenizer(s);
StringTokenizer st2 = new StringTokenizer(s, "\\d");

A primeira construção utiliza um delimitador padrão, que também é o espaço em branco. A segunda construção permite que se passe para o construtor o delimitador a ser utilizado. Para iterar entre os tokens, usamos o método hasMoreTokens(), para saber se há um novo token e nextToken() para obter o próximo token.

while (st.hasMoreTokens()){
System.out.println(st.nextToken());
}


A partir do java 1.5, temos mais uma maneira de lidar com tokens, fornecida pela classe Scanner. Seu construtor aceita tanto uma String quanto um InputStream ou mesmo um arquivo (File).

String s = "a3 5aab5haf"
Scanner scan = new Scanner(s);
File arq = new File("arquivo.txt");
scan = new Scanner(arq);

A classe Scanner fornece os métodos gerais hasNext() e next() para verificar se há mais tokens e retornar o próximo token, como um objeto String. Ela fornece também métodos mais especificos, como hasNextBoolean() ou hasNextInt() e os respectivos métodos de obtenção, nextBoolean(), nextInt() etc. Por default, a classe Scanner também usa o espaço em branco como delimitador.

Thursday, May 11, 2006

 

Regex

Para usar regex em java, nos valemos das classes Pattern e Matcher. Criamos uma expressão regular assim:

Pattern p = Pattern.compile("ab"); //onde "ab" é a expressão regular.

A String que servirá de base para a busca, criamos assim:

Matcher m = p.matcher("abaaaba");

Por fim, usamos os métodos find(), start(), group() de Matcher para obter as expressões capturadas pela expressão regular e o índice na string de base onde elas ocorrem.

boolean b = false;
while (b=m.find()){
System.out.print(m.start() + " "+m.group());//imprie o índice onde começa uma expressão capturada e o a própria expressão.
}

Metacaracteres relevantes:

\d -> digitos
\w -> caracteres alfanuméricos e underscore "_"
\s -> espaço
[abc] -> uma das letras a,b ou c
[a-f] -> uma letra em a e f
[a-fA-F] -> uma letra entre a e f, e A e F
+ -> uma ou mais ocorrências
* -> zero ou mais ocorrências
? -> zero ou uma ocorrência
. -> qualquer caractere
^ -> negação
*,+,? -> são interpretados gulosamente
*?,+?,?? -> são interpretados de maneira relutante
() -> Agrupa elementos

Exemplos:

padrão: .*xx
string: yyxxxyxx
Resultado: 0 yyxxxyxx

padrão: .*?xx
string: yyxxxyx
Resultado: 0 yyxx 4 xyxx

padrão: 0[xX]([0-9a-fA-F])+
string:12 0x 0x12 0xFF 0xg"
Resultado 6 0x12 11 0xFF

Um ponto importante a ser observado sobre os metacaracteres é que eles não podem ser atribuídos a uma string dessa maneira:

String qualquer="xxx\d";

Pois o compilador, ao encontrar \, vai procurar uma sequência de escape, no entanto, não existe a sequência de escape \d. Para que este erro de compilação não ocorra, o código deve ficar:

String qualquer="xx\\d"

Usamos uma contrabarra determinar que a próxima contrabarra deve ser lida literalmente.

Tuesday, May 09, 2006

 

Números, Datas e Formatação II

É por mei da classe Locale que obtemos facilidades para tratar a internacionalização. Para criar uma instância de Locale, temos basicamente duas opções:

Locale pt = new Locale("pt");
Locale ptBR = new Locale("pt","br");

Com o primeiro construtor obtemos um Locale relativo a uma língua, com o segundo, relativo a uma língua e a uma região onde essa língua é falada, o que é extremamente útil para tratar datas e valores em conformidade com os padrões de cada região. Por exemplo, o códgio a seguir

DateFormat dfbr = DateFormat.getDateInstance(DateFormat.FULL,ptBR);
Calendar c = Calendar.getInstance();
c.set(2006,05,09);
System.out.println(dfbr.format(c.getTime());

irá imprimir a data em sua forma mais extensa e em conformidade com os padrões brasileiros de apresentação de data, inclusive, traduzida para o português. Há mais dois métodos relevantes da classe Locale, por meio dos quais obtemos o nome da língua e do país representados por uma instância do Locale:

System.out.println(ptBR.getDisplayLanguage());
System.out.println(ptBR.getDisplayCountry());
System.out.println(ptBR.getDisplayLanguage(ptBR));
System.out.println(ptBR.getDisplayCountry(ptBR));

Os dois primeiros imprimem o nome da língua e do país no local default, que, geralmente, é o locale US. Neste caso, o resultado seria "portuguese" e "Brazil". Os dois últimos comandos imprimem no local passado como argumento, resultando, portanto, em "português" e "Brasil".

Por fim, a classe NumberFormat é usada para tratar números. Como DateFormat, ela é uma classe abstrata, portanto, para ter uma de suas instâncias, devemos fazer:

NumberFormat nf = NumberFormat.getInstance();
nf = NumberFormat.getNumberInstance();
nf = NumberFormat.getCurrencyInstance();

Cada um desses construtores é sobrecarregado para permitir o use de locales:

NumberFormat nf = NumberFormat.getInstance(ptBR);

Até o momento, não vi qualquer diferença entre getInstance() e getNumberInstance(), ambos retornam um formatador numérico de propósito geral, enquanto getCurrencyInstance(), um formator numérico para formatar valores financeiros. Sem o uso de locale, esses métodos retornam um formator orientado pelo locale default.
Um método importante dessa classe é setMaximumFractionDigits(), que serve para configurar o número de casas decimais após a vírgula na formação de um número de ponto flutuante. Por exemplo, se fizermos:

double d = 124.233213453;
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(3);
System.out.println(nf.format(d));

será impresso 124.233. NumberFormat também tem um método parse() e ele lança igualmente uma exceção caso a string passada não possa ser convertida para uma instância de Number.

Monday, May 08, 2006

 

Numeros, Datas e Formatação.

A classe Date está ultrapassada e deve-se usar Calendar para manipular datas. No entanto, ainda é últil usar,

Date data = new Date()

para obter uma representação do momento presente. A classe Calendar é abstrata, logo, não podemos criar uma instância dela diretamente com o comando,

Calendar calendario = new Calendar();

Devemos usar

Calendar calendario = Calendar.getInstance();

Podemos configurar a data atual representada por um objeto do tipo Calendar por meio de um objeto Date.

calendario.setTime(data);

Operações com datas são facilitadas pelos métodos roll() e add(). Para adicionar, à data atual, meses ou dias, fazemos:

calendario.add(Calendar.MONTH,1); \\será adicionado um mês à data atual.
calendario.add(DAY_OF_WEEK,3); \calendario.add(Calendar.HOUR,-5);\\a data atual será subtraída de 5 horas.
calendario.roll(Calendar.DAY_OF_WEEK,10);\\a data atual será acrescida de 10 dias.
\\no, entanto, se ela ultrapassar o limite do mês, este não será alterado.

A classe DateFormat provê dois métodos importantes para lidar com datas. format(Date d), que transforma um objeto do tipo Date em uma String e parse(String s) que transforma um objeto do tipo String em um objeto do tipo Date. Também a classe DateFormat é abstrata e, por isso, não pode ser instanciada diretamente. Ao invés, usamos:

DateFormat df = DateFormat.getInstance();
DateFormat df1 = DataFormat.getDateInstance();

A diferença entre ambos é que o método format() do primeiro irá produzir uma String que ela em consideração a data e a hora representados em um objeto Date, enquanto o segundo exprime apenas a data representada. O segundo método é sobrecarregado e permite que você passe como parâmetro um estilo para a formatação da data:

DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
df = DateFormat.getDateInstance(DateFormat.MEDIUM);
df = DateFormat.getDateInstance(DateFormat.LONG);
df = DateFormat.getDateInstance(DateFormat.FULL);

O método parse() lança a exceção ParseException e, por isso, só pode ser usado dentro da cláusula try com a captura da referida exceção. Se a String passada como argumento para o método parse() contém informações apenas sobre a data, mas não sobre a hora, então o objeto Date irá representar a data indicada e irá inicializar as horas e minutos como zerados.

Saturday, May 06, 2006

 

Construtores

Ao definir uma classe sem um construtor, o compilador irá definir implicitamente um construtor sem argumentos. Se, no entanto, você escreve um construtor, seja ele com ou sem argumentos, então o construtor não providencia nenhum construtor implicitamente. A primeira linha de um construtor deve ser super() ou this(). Se nenhum dos dois métodos é escrito explicitamente, o compilador irá inserir implicitamente super(). Ambas chamadas não podem ser declaradas no corpo do mesmo construtor, ou this(), ou super(). Repare, no entanto, que, em algum momento, haverá a chamada para o construtor da superclasse por meio do super(). Caso a superclasse não tenha um construtor sem argumentos, então você deverá chamar explicitamente, no construtor da classe herdeira, o construtor com argumentos da superclasse. Vejamos um exemplo:

public class Animal{
public Animal(String s){}
}

public class Dog extends Animal{}

O código acima não compila, pois o compilador irá gerar implicitamente um construtor na classe Dog que chama o construtor sem argumentos da superclasse por meio do super(), mas não há um construtor sem argumentos na classe Animal. Neste caso, o construtor de Dog deve ser explicitamente escrito e o construtor com argumentos da classe Animal chamado por meio de super(objeto_string).

Construtores não podem ter retornos e devem ter o mesmo nome da classe. O construtor providenciado pelo compilador, quando não há nenhum explícito na classe, tem o mesmo nível de acesso que a classe. Construtores podem ser declarados como private. Outro detalhe importante é que os métodos de uma instância só podem ser chamados depois que super() foi executados. O seguinte código, portanto, é ilegal:

public class Dog{
public Dog(){
eat();
this(5);
}
public Dog(int n){
}

public void eat();
}

O compilador irá adicionar implicitamente a chamada a super() no construtor com argumentos, mas o código não irá compilar ainda assim, pois no construtor sem argumentos há uma chamada a um método de instância, a saber, eat(), antes que ocorra a chamada de super. Antes do método super(), somente métodos estáticos podem ser chamados.

Wednesday, May 03, 2006

 

Sobrescrita

Um método herdade pode ser sobrescrito para permitir que a classe herdeira tenha comportamentos mais específicos que a superclasse. Um método sobrescrito não pode alterar a lista de parâmetros. Não pode também restringir o tipo de acesso. Um método declarado como public não pode ser sobrescrito pela classe herdeira como private. No entanto, pode-se alargar o tipo de acesso. Um método declarado como protected pode ser sobrescrito pela classe herdeira como public. O tipo de retorno não pode também ser modificado, a não ser trocado por um dos seus subtipos. Importante na situação de sobrescrita de métodos é reter também a idéia básica do polimofirsmo: em tempo de execução, o método chamado, por exemplo, por a.eat, onde a é declarado como Animal a; não é o método da classe declarada, mas do tipo instânciado. Se a se refere a um objeto do tipo Cavalo, que herda Animal, o método eat() chamado é da classe Cavalo.

O exemplo abaixo serve para ilustrar a situação de sobrescrita e também sobrecarga.


class Animal{

public Animal(String s){}

public void eat(Animal a){
System.out.println("Animal come");
}
}

class Cavalo extends Animal{

public Cavalo(String s){
super(s);
}

public void eat(Animal a){
System.out.println("Cavalo-Animal come");
}

public void eat(Cavalo c){
System.out.println("Cavalo come");
}

public static void main(String args[]){
Cavalo c = new Cavalo("c");
c.eat(c);
Animal a = new Animal("a");
a.eat(a);
a = new Cavalo("c");
a.eat(a);
}
}

O resultado da execução deste programa será:

Cavalo come
Animal come
Cavalo-Animal come

c.eat(c) chama o método eat() de Cavalo, que sobrescreve eat() de Animal. a.eat(a) chama o método eat() de Animal. Por fim, a.eat(a), onde a variável a, embora declarada como do tipo Animal, se refere, na verdade, a uma instância de Cavalo, chama o método sobrecarregado eat(Animal a) da classe Cavalo. A lógica aqui é a seguinte: o tipo de objeto referido pela variável a determina de qual classe o método será chamado. No caso, da classe Cavalo. E o tipo declarado da variável a determina qual dos métodos sobrecarregados da Classe Cavalo será chamado.

 

enums

Enums podem ser declarados como uma classe ou no interior de outra classe, desde que esta última seja declarada como public ou default. Enums não podem ser declarados no interior de um método.

enum Marcas {FIAT, FORD, FERRARI}

public class UsaMarcas{
public static void main(String args[]){
Marcas marca = Marcas.FIAT;
}
}

O código abaixo também é legal:

public class UsaMarcas{

enum Marcas {FIAT, FORD, FERRARI}

public static void main(String args[]){
Marcas marca = Marcas.FIAT;
}
}

A sintaxe do enum permite que ele seja declarado com um ponto e vírgula ao final. É optativo colocá-lo:

enum Marcas {FIAT, FORD, FERRARI};

Enums podem ter construtores e métodos, permitindo, assim, um comportamento mais flexível:

enum Marcas {FIAT("uno"), FORD("Ka"), FERRARI("F40");
Marcas(String modelo){ this.modelo = modelo;};
private String modelo;
public String getModelo(){return modelo;}
}

public class UsaMarcas{
public static void main(String args[]){
Marcas marca = Marcas.FIAT;
System.out.println(marca.getModelo());
}
}

O construtor de um enum não pode ser chamado diretamente. Isso não existe:

Marcas marca = new Marcas(FIAT,"uno");
Marcas marca = new Marcas.FIAT("uno");

Métodos do enum podem ser sobrescritos após a declaração de um dos seus valores. Imagine que desejamos que apenas o valor FERRARI retorne "classe A" no metodo
getTipo() e os outros valores do enum Marcas retornem "classe B". Neste caso,
fazemos assim:

enum Marcas {FIAT("uno"), FORD("Ka"), FERRARI("F40"){
public String getTipo(){return "classe A";}
};
Marcas(String modelo){ this.modelo = modelo;};
private String modelo;
public String getModelo(){return modelo;}
public String getTipo(){return "classe B";}
}

public class UsaMarcas{
public static void main(String args[]){
Marcas marca = Marcas.FIAT;
System.out.println(marca.getModelo());
System.out.println(marca.getTipo());
marca = Marcas.FERRARI;
System.out.println(marca.getTipo());
}
}

A execução desse código resultaria:

uno
classe B
classe A

Sobrescrevemos o método desejado imediatamente após a declaração do valor do enum.

enum Marcas {FIAT("uno"), FORD("Ka"){
public String getTipo(){return "classe A";}
}, FERRARI("F40");
Marcas(String modelo){ this.modelo = modelo;};
private String modelo;
public String getModelo(){return modelo;}
public String getTipo(){return "classe B";}
}

Archives

March 2006   April 2006   May 2006   July 2006   August 2006  

This page is powered by Blogger. Isn't yours?