Introducción á Linguaxe Java

De Wiki do Ciclo ASIR do IES de Rodeira
Saltar á navegación Saltar á procura

Introducción á Programación Orientada a Obxecto.

Todas as linguaxes de programación pretenden acercar a programación das computadoras á linguaxe humana. A linguaxe ensamblador permite enfocar a programación como unha serie de movementos de información e de operacións matemáticas permitíndonos esquecernos do funcionamento electrónico da máquina; un paso máis son as linguaxes imperativas (C, COBOL, FORTRAN, Pascal ...), que permiten acercar aínda máis a programación á linguaxe humana. Sen embargo, a programación coas linguaxes imperativas aínda esixe que pensemos en termos da estructura dos computadores máis que en termos da estructura do problema a resolver.

A programación orientada a obxectos (OOP) pretende proporcionar utilidades ó programador para representar os distintos elementos que interveñen nun problema. Deste xeito, un programa sería un conxunto de obxectos interactuando entre eles.

Por exemplo, un programa que controlara automaticamente a luz dunha habitación podería constar dun obxecto lámpada e un obxecto sensor. O obxecto sensor enviaría ó obxecto lámpada a mensaxe on cando a luz baixa dun límite e off cando sube dese límite.

Ficheiro:Lampada.gif
Envío de Mensaxes entre obxectos

Os obxectos almacenan datos é se lles pode facer peticións para que realicen operacións sobre si mesmo. En teoría pódese coller calquera compoñente conceptual do problema (edificios, sofás, lámpadas, ... etc) e representalos mediante un obxecto no programa.

Existen distintas aproximacións á programación orientada a obxectos, en C++ por exemplo, pode escribirse código "de programa" sen formar parte de ningún obxecto, e en JAVA Todo é un obxecto.

  • Un programa é un conxunto de obxectos dicíndolle uns ós outros qué facer mandándolles mensaxes. En realidade as mensaxes consistirán en chamar funcións pertencentes ó obxecto ó que lle mandamos a mensaxe.
  • Cada obxecto ten a súa propia memoria composta doutros obxectos. É dicir, pódense crear novos obxectos xuntando varios obxectos xa existentes, aumentando a complexidade do programa ó mesmo tempo que permanece oculta nos novos obxectos.
  • Cada obxecto ten un tipo ou 'clase'. Ou sexa, cada obxecto é un elemento pertencente a unha clase determinada. A característica máis importante dunha clase son as mensaxes que pode recibir (as súas funcións).

As mensaxes as que pode responder un obxecto atópanse especificadas na súa interface, que ven imposta polo tipo de obxecto (a súa clase). De aquí en diante especificaremos os obxectos do seguinte xeito:

Ficheiro:Lampada1.gif
Representación dun obxecto

Por exemplo, en JAVA un obxecto deste tipo utilizaríase do seguinte xeito:

Lampada Luz = new Lampada();
Luz.On();

Neste caso, Lámpada é o nome da clase (o tipo) e Luz é un 'recipiente' onde se pode almacenar unha referencia a un obxecto concreto dese tipo. Mediante o operador new créase un novo obxecto do tipo Lámpada e almacénase a referencia en Luz. A partir dese instante pódense enviar mensaxes o novo obxecto utilizando Luz. Por exemplo 'On' prende a lámpada.

As mensaxes que se lle poden enviar a un obxecto reciben o nome de métodos da clase (en realidade son funcións que operan sobre a propia clase). Ademais, un obxecto pode estar composto por outros obxectos que reciben o nome de membros da clase ou atributos.

Entre os programadores nas linguaxes orientadas a obxecto pódese distinguir entre os creadores de clases e os programadores cliente, de xeito parecido ós creadores de librerías e os usuarios das mesmas nas linguaxes tradicionais. Os creadores de clases encárganse de desenvolver novas clases de obxectos de utilidade xeral, mestras que os programadores cliente utilizarán estas para desenvolver o seu proxecto particular.

Unha das maiores vantaxes da programación orientada a obxectos é que permite a reutilización de código con gran facilidade ademais de simplificar tamén o mantemento do mesmo. Como as aplicacións non son máis que un conxunto de obxectos enviándose mensaxes, é posible modificar os obxectos de forma independente sen afectar ó resto do programa. Tamén é posible verificar o comportamento dos obxectos de forma individual, e localizar o obxecto erróneo con facilidade.

Para conseguir todo esto é necesario facer que o usuario da clase non poda modificar partes críticas do obxecto que poidan afectar o correcto funcionamento do mesmo, e dicir, separar o interface da clase da súa implantación, ocultando o cliente o código de funcionamento interno da clase (Unicamente nos interesa que unha lámpada se pode prender, non cómo). Para facer esto as linguaxes orientadas a obxecto posúen unha serie de modificadores de acceso, que en JAVA son public, private e protected, ademais do acceso por defecto que se chama friendly.

  • public : Todas as outras clases teñen acceso os métodos ou membros public.
  • private : Unicamente se pode acceder os métodos ou membros private desde dentro da propia clase.
  • protected : Unicamente se pode acceder os métodos ou membros protected desde dentro da propia clase ou desde unha clase herdada.
  • friendly : Unicamente se pode acceder os métodos ou membros friendly desde as clases desenvolvidas no mesmo package.

Unha vez que temos unha clase creada e probada, debería ser unha unidade de código útil e reutilizable. A forma máis simple de reutilizar unha clase e creando obxectos desa clase, pero tamén é posible colocar un obxecto desa clase dentro dunha clase nova. Unha clase pode estar composta de calquera número de outros obxectos.

Esta forma de crear novas clases chámase composición ou agregación, e a miúdo se indica como unha relación 'ten un', por exemplo, un coche ten un motor, e dicir que o obxecto coche ten un membro que é un obxecto motor.

Ficheiro:Motor.gif
Composición/Agregación de obxectos

A Herdanza

Outro concepto importante na programación orientada a obxectos é a herdanza. Moitas veces precisamos dunha nova clase que se comporte de xeito moi similar a unha xa existente, co que sería bo ter un xeito de facer unha copia da clase orixinal e logo facer modificacións e engadidos á copia para ter a nova clase. Esto conséguese mediante a herdanza. A clase orixinal recibe o nome de clase base, pai ou super, mentres que a nova clase chámase clase derivada ou clase filla.


Unha vez que derivamos unha clase herdaremos o seu interface e membros de clase, e poderemos reescribir os métodos que queiramos para modificar o seu comportamento. Os métodos que non reescribamos manterán o comportamento da clase base. Este tipo de relación chámase tamén 'é un'. Por exemplo: Un gorrión é un paxaro.

Tamén é posible engadirlle novos métodos ó interface da clase derivada ampliando así a súa funcionalidade. Neste caso a relación chámase 'é coma un'. Cando teñamos que engadir novos métodos a unha clase derivada paga a pena cuestionarse si eses novos métodos poden pertencer en realidade a clase base.

Vexamos un exemplo:

Tomemos a clase figura xeométrica 'Figura'. Cada 'Figura' ten uns atributos (obxectos membros): tamaño, cor, e posición e é posible realizar sobre ela varias operacións distintas: debuxala, borrala, movela, coloreala ou rotala. A partir de aquí podemos desenvolver novas clases coma 'Circulo', 'Cadrado' e 'Triangulo', que a súa vez son 'Figuras', e polo tanto poderán automaticamente debuxarse, moverse, borrarse, colorearse ou rotarse, ademais de ter un tamaño unha cor e unha posición. Cando creemos un novo obxecto de tipo 'Círculo' poderemos tratalo como un 'Círculo' ou como unha 'Figura', xa que os 'Círculos' son 'Figuras'. O tratar un obxecto dunha clase derivada coma si fora da clase base chámase upcasting.

Ficheiro:Herdanza1.png
Exemplo de Herdanza

Se a forma de debuxarse, moverse, borrarse, colorearse ou rotarse non coincide coa forma en que o fan as 'Figuras' en xeral, será necesario reescribir o método na clase derivada (sobreposición de métodos). Tamén é posible escribir varias versións dun mesmo método dentro dunha clase, o que recibe o nome de sobrecarga de métodos sempre e cando os argumentos que reciban os métodos sexan distintos, xa que o intérprete utilizará os argumentos para elixir o método a chamar.

As veces non é posible implantar todos os métodos dunha clase por ser demasiado xeral, servindo esta unicamente como base para derivar novas clases. Unha clase que non ten implantada toda a súa interface recibe o nome de clase abstracta, e o método que non está programado chámase método abstracto. Tamén é posible definir clases que non teñan definido ningún dos métodos do seu interface, vendo a ser un 'molde' que teñen que cumprir todas as clases derivadas. Estas clases denomínanse interfaces.

No caso particular de JAVA, todas as clases que se poden utilizar derivan dunha clase xenérica chamada Object, o que lles da a tódolos obxectos algunhas características comúns. JAVA é polo tanto unha linguaxe con unha xerarquía de clases con unha soa raíz, a diferencia de outras linguaxes de raíz múltiple como C++. Co JDK de JAVA proporciónase unha extensa librería de clases que cobren a maior parte das necesidades básicas de calquera programador.

NOTA
En algunhas linguaxes unha clase pode derivar de varias clases (herdanza múltiple), sen embargo unha clase en JAVA so pode ter unha clase base (herdanza sinxela).

É moi importante coñecer a xerarquía de clases dun obxecto, xa que nos proporcionará a información necesaria sobre o seu uso e posibilidades.

Ficheiro:Xerarquia.png
Xerarquía de Clases

Coma xa vimos antes é posible crear novas clases derivando a partir dunha clase base. Deste xeito podemos ter a clase 'Figura' e derivar dela a clase 'Triangulo' co que un 'Triangulo' é unha 'Figura'. A causa de esto é posible tratar a un obxecto da clase 'Triangulo' coma un 'Triangulo' ou como unha 'Figura' segundo nos conveña (ollo, o contrario non é certo xa que unha 'Figura' non ten por que ser un 'Triangulo'). Tendo en conta esto... ¿ Qué sucedería no caso seguinte ?

Ficheiro:Paxaros.png
¿Que ocurre neste caso?

Coma os pingüíns teñen un xeito de moverse distinto a maior parte dos paxaros é necesario reescribir o método de moverse, o que non é o caso da pomba. O programa principal invoca ó método move () da clase PAXARO, e tanto os obxectos PINGÜÍN como POMBA son tamén obxectos PAXARO. ¿ A qué método move () se chamará ?. Dende o punto de vista do programador é irrelevante o método que se invoque, xa que o único que lle interesa é que o paxaro se mova, non cómo. Por outra banda o compilador non pode sabelo en tempo de compilación, xa que depende do obxecto PAXARO de que se trate. A solución que toman as linguaxes orientadas a obxecto consiste en non determinar a dirección do método o que se vai a saltar hasta que se execute o programa, sendo en tempo de execución cando se decide a función a chamar. Podes ver mellor o funcionamento executando o seguinte exemplo:

public class ClaseBase {

  void Nome () {
    System.out.println("Son a clase BASE");
  }

  /* 
      Método Inicial do Programa
  */
  public static void main (String[] args) {

    ClaseBase n,n1,n2,n3;

    n= new ClaseBase(); n.Nome();
    n1=new ClaseDerivada1(); n1.Nome();
    n2=new ClaseDerivada2(); n2.Nome();
    n3=new ClaseDerivada3(); n3.Nome();
  }

}

class ClaseDerivada1 extends ClaseBase {

  void Nome () {
    System.out.println("Son a Clase Derivada1");
  }
}


class ClaseDerivada2 extends ClaseBase {
  void Nome () {
    System.out.println("Son a Clase Derivada2");
  }
}

class ClaseDerivada3 extends ClaseBase {

}

Constructores e Destructores

Como podemos observar no exemplo anterior, para crear un obxecto en JAVA é necesario utilizar unha instrucción especial chamada new. En JAVA todas as declaracións son referencias a obxectos (é dicir, unha variable que almacenará a dirección de memoria onde se atopa o obxecto referenciado) e é necesario crear os obxectos mediante chamadas explícitas a new mentres que outras linguaxes coma C++ si que permiten crear obxectos mediante unha simple declaración.


Sen embargo o que tódalas linguaxes orientadas a obxecto teñen en común é a existencia dos métodos constructores. Un método constructor invócase de xeito automático no instante en que se crea un novo obxecto. Os métodos constructores en case todas as linguaxes orientadas a obxectos teñen o mesmo nome que a clase.

 Se nós o escribir a clase non proporcionamos un método constructor o sistema creará ún por defecto. 

Por suposto, é posible sobrecargar o método constructor de xeito que teñamos varias formas de crear un obxecto. Cando nós creamos un novo obxecto, invocaranse de xeito automático os métodos constructores de toda a xerarquía de clases asociada comezando pola raíz, xa que un obxecto derivado é ademais un obxecto da clase pai.... etc.

A maior parte das linguaxes orientadas a obxecto teñen tamén outro método especial destinado a liberar toda a memoria utilizada por un obxecto cando é destruído. Este método que se invoca de xeito automático o destruír un obxecto chámase destructor. JAVA, sen embargo non ten métodos destructores, xa que a memoria é liberada de xeito automático cando é necesario mediante un recolector de lixo. O recolector de lixo encárgase de eliminar os obxectos que non están a ser utilizados cando o sistema ande baixo de memoria. Esto, aínda que é unha gran vantaxe para o programador (evita que nos poidamos esquecer de liberar a memoria), é tamén un problema, xa que non temos forma de saber o instante en que un obxecto é eliminado. Podes observar todo o proceso executando o seguinte exemplo:

public class ClaseBase {
  static int i;

  ClaseBase() { // Constructor
    i++; 
    System.out.println("Creando Obxecto ClaseBase "+i);
  }

  void Nome () {
    System.out.println("Son a clase BASE");
  }

  /*
     Método de Inicio
  */
  public static void main (String[] args) {
    ClaseBase n,n1,n2,n3;

    n= new ClaseBase(); n.Nome();
    n1=new ClaseDerivada1(); n1.Nome();
    n2=new ClaseDerivada2(); n2.Nome();
  }

}

class ClaseDerivada1 extends ClaseBase {
  
  ClaseDerivada1() {
    System.out.println("Creando Obxecto ClaseDerivada1 ...");

  }

  void Nome () {
    System.out.println("Son a Clase Derivada1");
  }
}

class ClaseDerivada2 extends ClaseBase {
  void Nome () {
    System.out.println("Son a Clase Derivada2");
  }
}

Novas Características de J2SE 5.0

  • Engadir tipos parametrizados. JDK 1.5
  • Engadir nova forma do for
  • Conversión Automática de tipos primitivos
  • enums
  • static imports
  • Metadatas
  • Número variable de argumentos
  • Salida por pantalla con formato (printf)
  • Nueva clase Scanner para entrada con tipos
  • java.util.concurrent

Introducción á Linguaxe Java

JAVA é unha linguaxe orientada a obxecto independente da plataforma; unha aplicación escrita en JAVA funcionará baixo calquera máquina e sistema operativo. O compilador JAVA xera un 'código ensamblador' que se interpreta por un intérprete JAVA (este tipo de códigos non asociados con ningún procesador físico chámanse byte-codes).

O intérprete JAVA implementa unha 'máquina virtual JAVA' que é unha máquina simulada por software que executa o 'código ensamblador JAVA'. Hoxe en día a maior parte dos navegadores WEB dispoñen dunha máquina virtual JAVA, moitas veces dispoñible como un módulo (plug-in) que pode ser actualizado por separado.

A independencia da plataforma que proporciona JAVA ten un prezo en velocidade nas aplicacións xa que é necesario interpretar as instruccións en byte-codes e traducilas en chamadas a instruccións nativas. Para minimizar esta perda de tempo moitas máquinas virtuais empregan JIT (Just In Time), que consiste en almacenar en memoria as instruccións traducidas o ensamblador nativo de xeito que unicamente se interpretarán a primeira vez que son levadas a cabo.

Como todas as linguaxes de programación, JAVA proporciona unha extensa biblioteca de clases de propósito xeral que facilita moito a programación de aplicacións. Estas clases atópanse agrupadas por 'tipo de utilidade' no que se chama 'paquetes' ou packages. Cando nós desenvolvemos unha nova clase podemos indicar o package do que vai formar parte, e se non o indicamos pasará a formar parte do 'package por defecto'. Para utilizar as clases pertencentes a un package en concreto será necesario importar o package mediante unha instrucción import.

JAVA é unha linguaxe sensitiva ás maiúsculas e minúsculas, de xeito que Variable é un identificador distinto de variable. Ademais é preciso seguir unha serie de regras estrictas na creación do código fonte:

  1. Cando desenvolvamos unha aplicación unicamente pode haber unha clase pública dentro de un mesmo ficheiro fonte.
  2. Os ficheiros fonte deberán chamarse exactamente igual que a clase pública que conteñen e con extensión .java.
  3. Cando compilemos un ficheiro fonte o compilador xerará un ficheiro compilado con extensión .class por cada clase definida.
  4. O intérprete de java invocarase co nome da clase publica como argumento para iniciar a aplicación.

Este xeito de traballar facilita a actualización de aplicacións sustituindo unicamente as clases modificadas e facendo innecesaria a recompilación do código que non cambiou.

Outra característica de JAVA é que todo é un obxecto que descende da clase raíz Object. Sen embargo, para manipular os obxectos o que utilizaremos serán referencias (é dicir, variables que almacenarán a dirección do obxecto), por exemplo:

<java>

 String nome;

</java>

Definirá unha variable que poderá almacenar unha referencia a un obxecto da clase String. Se quixeramos enviar unha mensaxe ó obxecto nome produciríase un erro, xa que nome non está referenciando ningún obxecto. Para crear obxectos utilízase o operador new invocando un constructor da clase:

<java>

 String nome=new String("Introducción");

</java>

Deste xeito creamos a variable para referenciar un obxecto da clase String, creamos o obxecto da clase String e o almacenamos na variable nome. Tamén é posible facelo do seguinte xeito:

<java>

 String nome;
 nome = new String("Introducción");

</java>

É polo tanto importante ter en conta que estaremos sempre manexando referencias a obxectos.

Para acelerar a execución das aplicacións, JAVA proporciona unha serie de tipos de datos primitivos que non son clases, evitando así todo o proceso de creación das clases almacenando a información na pila do programa. Os datos primitivos non son referencias, se non que as variables almacenan o valor directamente. Sen embargo por cada tipo primitivo JAVA ten unha clase asociada que é equivalente:

Tipo Primitivo

Valor por defecto

Tamaño en Bytes

Mínimo

Máximo

Clase asociada

boolean

false

--

true, false

true, false

Boolean

char

'\u0000' (null)

16 - bit

Unicode 0

Unicode 216-1

Character

byte

0

8 - bit

-128

+127

Byte

short

0

16 - bit

-215

+215-1

Short

int

0

32 - bit

-231

+231-1

Integer

long

0

64 - bit

-263

+263-1

Long

float

0.0

32 - bit

IEEE754

IEEE754

Float

double

0.0

64 - bit

IEEE754

IEEE754

Double

void

--

--

--

--

Void

A visibilidade dos datos primitivos e dos obxectos ven dada polos delimitadores { e }, sendo visible dentro dos delimitadores no que está definido.

Ademais destes tipos primitivos JAVA incorpora dúas clases para realizar aritmética de alta precisión (BigInteger e BigDecimal) cos que se poden realizar exactamente as mesmas operacións que cos tipos primitivos e que poden almacenar números de calquera tamaño sen perder información.

A diferencia de outras linguaxes orientadas a obxecto, en JAVA non se liberan de forma específica os obxectos creados, se non que é o sistema o que decide cando é necesario liberar a memoria ocupada polos obxectos que non están a ser utilizados mediante o chamado recolector de lixo.

Como se pode observar na táboa anterior, existen unha serie de valores por defecto que tomarán as variables de clase no caso de non ser inicializadas. Sen embargo, co obxecto de evitar erros, as variables dos métodos teñen que ser inicializadas antes de poder utilizalas.

A inicialización pode realizarse no mesmo instante da definición, ou ben cunha sentencia posterior. O seguinte exemplo mostra a orde de inicialización das variables de clase.

<java> class Elemento {

 Elemento(int num) {
    System.out.println("Creando elemento "+num);
 }

}

class Proba {

 Elemento e1=new Elemento(1);
 
 Proba() {
   System.out.println("Creando obxecto Proba");
   e3=new Elemento(33);
 }

 Elemento e2=new Elemento(2);
 void funcion() {
   System.out.println("Chamando a Proba.funcion()");
 }
 Elemento e3=new Elemento(3);

}

public class OrdeInicio {

  public static void main(String[] args) {
     Proba p=new Proba();
     
     p.funcion();
  }

} </java>

Creación de Clases e de Métodos.

Para crear novas clases utilizaremos a palabra reservada class do seguinte xeito:

<java> class NomedaClase {

 /* Definición da clase */

} </java>

Cando definimos unha clase (e unha aplicación JAVA non é máis que definicións de clases, creación de obxectos desas clases e envío de mensaxes entre os obxectos) é posible poñer dentro dela dous tipos distintos de elementos: datos (atributos) e funcións (métodos).

Os métodos son funcións definidas do seguinte xeito:

<java> TipodeDatoaVoltar NomedeFunción ( /* lista variables argumento separadas por comas */) {

 /* Corpo da función */

} </java>

para voltar o dato de retorno da función utilízase return nomeVariable;, ou simplemente return si a función é void.

Os datos son definicións de variables da forma: tipodedato nomevariable;

Se os datos son tipos primitivos terán o valor por defecto indicado anteriormente, se non, serán referencias a un obxecto da clase tipodedato que aínda non referencian a ningún obxecto en concreto. Para inicializar os datos membros dunha clase utilízase normalmente un método constructor. Un método constructor é un método especial que se chama automaticamente ó crear un obxecto dunha clase, ten que chamarse exactamente igual que a clase e devolve un obxecto desa mesma clase (aínda que non se pon nada especificamente). Todas as clases teñen un método constructor, e se nós non o escribimos JAVA o crea por defecto.

Unha vez definida unha clase poderemos crear novos obxectos desa clase mediante o operador new do seguinte xeito:

<java>

 NomedaClase n;
 n=new NomedaClase (/* Argumentos do constructor */);

</java>

Co que crearemos un novo obxecto cos seus propios datos e métodos.

Unha posibilidade tanto dos métodos como dos datos é ser de tipo static. Un dato ou método static recibe o nome de dato ou método de clase, xa que existe aínda que non se cree ningún obxecto desa clase, e ademais ese dato é común a tódolos obxectos desa mesma clase e ós seus descendentes.

Tamén é posible crear novas clases derivando dunha clase xa existente. Nese caso a nova clase terá tódalas características da clase da que deriva, ademais de poder engadirlle novos métodos, novos datos ou redefinir métodos xa existentes.

Para derivar unha clase de outra emprégase a palabra clave extends do seguinte xeito:

<java> class NomedaClase extends NomedaClaseBase {

 /* Definición da clase */

} </java>

En JAVA unha clase unicamente pode derivar de outra, e dicir é de herdanza simple. Vexamos un exemplo de programa Java:

<java> // O primeiro programa JAVA

import java.util.*;

public class HolaMundo {

  public static void main (String[] args) {
     System.out.println("Hola Mundo. Hoxe e: ");
     System.out.println(new Date());
  }

} </java>

Para probar o exemplo este código debe chamarse HolaMundo.java e ó compilalo con javac producirá o ficheiro HolaMundo.class. Para executalo invocaremos o intérprete JAVA do seguinte xeito: java HolaMundo. O intérprete executará entón o método main que atope dentro da clase principal do módulo, neste caso da clase HolaMundo. En Java os ficheiros teñen que chamarse igual que a clase pública que conteñen, e un ficheiro so pode ter unha clase pública.

Vexamos outro exemplo:

<java> import java.util.*;

class Mundo {

  Date creacion;
  String nomeMundo;
  long distanciaSol;
  long masa;
  Mundo () {
    this ("Descoñecido",0,0);
  }

  Mundo (String nome) {
    this (nome,0, 0);
  }
  Mundo (String nome, long dSol, long m) {
     nomeMundo=nome;
     distanciaSol=dSol;
     masa=m;
     creacion=new Date();
     System.out.println("Creando "+nome+" en "+creacion);
  }
  
  String nomeMundo() {
     return nomeMundo;
  }

}

public class Satelite extends Mundo {

 long distanciaPlaneta;
 Mundo planeta;
  
 Satelite (Mundo p, long d, String nome) {
   planeta=p;
   distanciaPlaneta=d;
   nomeMundo=nome;
 }
 public static void main (String[] args) {
   Satelite s=new Satelite(new Mundo("Terra"),20000,"Lúa");
   System.out.println("Creado o satélite "+s.nomeMundo()+" Fin.");
 }

} </java>

Neste exemplo convén observar a presencia de 3 versións de constructor para a clase Mundo. JAVA seleccionará automaticamente o constructor axeitado. Outra novidade e o uso do operador this, que é unha referencia ó obxecto actual. Neste caso utilízase para invocar desde un constructor a outro.

Outro exemplo, pero utilizando unha [gal:GUI|GUI]:

<java> import java.awt.*; import javax.swing.*;

public class Hola extends JFrame {

  Hola () {
     JPanel p=new JPanel();
     p.add(new JLabel("Hola Mundo"));
     setContentPane(p);
     setVisible(true);
  }
  public static void main(String[] args) {
     new Hola();
  }

} </java>

Comentarios e documentación embebida

En JAVA pódense incluír no código comentarios dunha soa liña:

<java> // Esto é un comentario dunha liña </java>

ou comentarios de varias liñas:

<java> /* Esto é un

  comentario de
  varias liñas. 
*/

</java>

Pero a característica máis importante é a posibilidade de xerar documentación sobre o código a partir dos comentarios mediante unha utilidade chamada javadoc, que producirá un documento HTML. Todos os comandos de javadoc comezan por /** e finalizan con */. Existen tres tipos de documentación que corresponden co elemento ó que precede o comando: de clase, de dato ou de método. Soportan HTML embebido e unha serie de etiquetas cun significado especial :

<java> @see nome de clase </java>

Pode incluírse nun comentario de clase, de variable ou de método e especifica unha referencia a unha clase relacionada.

Etiquetas de clase

<java> @version información da version @author información do autor </java>

Etiquetas de Método

<java> @param nome de parámetros descrición @return descrición @throws nome completo de clase descrición @deprecated </java>

Operadores

ARITMÉTICOS

LÓXICOS

DE BIT


+ (suma)

== (igual que)

& (AND bit a bit)

- (resta)

!= (distinto de)

| (OR bit a bit)

* (multiplicación)

> (maior que)

^ (XOR bit a bit)

/ (división)

< (menor que)

~ (NOT bit a bit)

% (resto da división)

>= (maior ou igual que)

<< (xiro de bits)

= (asignación)

<= (menor ou igual que)

>> (xiro de bits)

++ (autoincremento)

&& (and)

>>> (xiro de bits sen signo)

-- (autodecremento)

|| (or)



! (not)



Os operadores utilízanse unicamente cos tipos de datos primitivos a excepción do operador = e o operador +, que pode ser utilizado con obxectos de tipo String para facer concatenacións de cadeas de carácteres. Outro operador que se pode utilizar con tipos non primitivos e os de comparación == e !=, pero é moi importante ter en conta o seguinte:


En JAVA para manexar os obxectos utilízanse referencias, ou sexa que si nós definimos un novo obxecto:


Obxecto obj= new Obxecto();


o que teremos na variable obj é unha referencia ó novo obxecto creado (a súa dirección de memoria).


Si eu quero almacenar unha copia do obxecto en outra variable non será correcto facer Obxecto obj1=obj;, xa que o único que fago é almacenar en obj1 unha referencia ó mesmo obxecto que ten obj. Unha posible forma de solucionalo e construír un método de copia.

Si eu creo un segundo obxecto e os comparo, o que estou a facer é comparar dúas direccións de memoria que probablemente non é o que eu quero facer. Neste caso, o operador == unicamente será true si as dúas referencias apuntan ó mesmo obxecto, non si os obxectos son iguais (de igual contido). Do mesmo xeito != será true sempre que as dúas referencias non apunten ó mesmo obxecto. Para solucionar este problema todos os obxectos incorporan un método chamado equals, que devolve true si os dous obxectos son iguais (de igual contido) e falso si son distintos.


Exemplo:

String str1=new String("Proba 1");

String str2=new String("Proba 2");


str1.equals(str2) // ou str2.equals(str1) e devolvería false


Tanto os operadores aritméticos como a nivel de bit pódense combinar co operador de asignación (=) para abreviar (por exemplo x+=30, ou x>>=2). JAVA tomou de C os operadores de autoincremento (++) e autodecremento (--). Estes dous operadores son unarios (operan con un so termo) e aumentan en 1 ou restan 1 á variable a que acompañan:


Exemplo:

int x=0;


++x; // (x pasa a valer 1

--x; // x volve a valer 0


Si o operador antecede á variable dentro dunha expresión, primeiro efectúase o incremento ou decremento e posteriormente devólvese o valor. Se o operador vai despois da variable primeiro devólvese o valor da variable e posteriormente increméntase ou decreméntase o valor.


Exemplo:

int x=0;


System.out.print("X = ");

System.out.println(++x);

System.out.println(x++);

System.out.println(x);


Control de execución


As sentencias de control de execución son iguais que as que tan o C:


if- else e o operador ?:

if (condición) {

/* instruccións */

}

[ else {

/* instruccións */

} ]










condición ? valor1 : valor2;


(Si a condición se cumpre devolve o valor1, se non o valor2)







return

Sirve para saír dun método voltando un valor. Si o método é void indicarase unicamente return;, noutro caso terá que indicarse return valor;


while

while (condición) {

/* instruccións */

}








do-while

do {

/* instruccións */

} while (condición);






for (/*instruccións iniciais separadas por comas*/; condición; /*instruccións separadas por comas*/) {

/* instruccións a repetir */

}



for


break e continue


break fai que o programa salga dun bucle de execución e continúe forza o comezo dunha nova repetición.


switch

switch (varible de tipo primitivo) {

case valor1: /* instruccións */

break;

...

default: /* instruccións */

break;

}













Exemplo:


class Alimento {

String nome;

int calorias;

boolean carne;


Alimento (String n, int enerxia, boolean c) {

nome=n; calorias=enerxia;

carne=c;

}

boolean eCarne() {

return carne;

}

String getNome() {

return nome;

}

int getCalorias() {

return calorias;

}

}


class Dragon {

String nome;

String cor;

boolean femia;

int forza;


Dragon (String n, String c, boolean s) {

nome=n; c=cor;

femia=s; forza=0;

System.out.println("Hola, son "+nome);

}

void come (Alimento comida) {

if (!comida.eCarne())

System.out.println("Puaaaagh, "+comida.getNome());

else {

System.out.print("\n Yuuuuummm, un ");

System.out.println(comida.getNome());

forza+=comida.getCalorias();

System.out.println("ñam, ñam, ñam");

}

}

void corre() {

while (forza >= 50) {

System.out.print("/\\ \\/ /\\ ");

forza-=50;

}

System.out.println(" Bufff, estou canso. TEÑO FAME !!!");

}

}


public class Corre {

public static void main(String[] args) {

Alimento cebola=new Alimento("Cebola",3,false);

Alimento laranxa=new Alimento("Laranxa",5,false);

Alimento gusano=new Alimento("Gusano",3,true);

Alimento rato=new Alimento("Rato",5,true);

Alimento vaca=new Alimento("Vaca",300,true);

Alimento cerdo=new Alimento("Cerdo",180,true);

Alimento cabra=new Alimento("Cabra",80,true);

Dragon draco=new Dragon("Draconia","Azul",true);


draco.corre();

draco.come(cebola); draco.corre();

draco.come(gusano); draco.corre();

draco.come(rato); draco.corre();

draco.come(laranxa); draco.corre();

draco.come(cabra); draco.corre();

draco.come(cebola); draco.corre();

draco.come(vaca); draco.corre();

draco.come(cerdo); draco.corre();

draco.come(rato); draco.corre();

}

}

Táboas


As táboas son obxectos que son tratados como todos os obxectos. Para declarar unha táboa é necesario:


  • Declarar unha variable para que conteña a táboa

Exemplo: Unha táboa de Strings.... String[] t;

  • Crear a táboa e asignala á variable

Exemplo: creo 10 strings... t = new String[10];


Unha vez feito eso é posible almacenar valores nos distintos elementos da táboa (t[0], t[1] ...). Tamén é posible crear a táboa segundo se fai a declaración....


String[] t=new String[10];


e incluso inicializar a táboa ó mesmo tempo que se declara :


String[] semana={"Luns","Martes","Mércores","Xoves",Venres","Sábado","Domingo"};


Da mesma forma que na linguaxe C, os índices das táboas comezan por 0, e é posible declarar táboas de varias dimensións:


int[][] Coords = new int[12][12];



As táboas teñen un método length que nos devolve o número de obxectos que ten a táboa.


Exemplo:

public class Proba {


public static void main (String[] args) {


String p[][]=new String[10][2];


System.out.println(args.length+" Argumentos");

System.out.println(p.length+" P Elementos");

System.out.println(p[0].length+" P[] Elementos");

}

}


Para axudar na manipulación das táboas JAVA proporciona unha clase chamada Arrays, que ten unha serie de métodos static que permiten varias operacións de unha forma cómoda.


Como podemos observar nos exemplos anteriores, as aplicacións comezan a súa execución chamando o método main da clase invocada polo intérprete. O método main ten un argumento que é unha táboa de Strings, contendo cada elemento un parámetro dos indicados na liña de comandos. O seguinte exemplo o ilustra:


Exemplo:

public class Proba {


public static void main (String[] args) {


int i;


System.out.println(args.length+" Argumentos");

for (i=0;i<args.length;i++)

System.out.println("Arg "+i+" = "+args[i]);

}

}


Operadores this e super


this é unha palabra reservada que representa unha referencia ó obxecto actual. Normalmente utilízase para chamar a un constructor dende outro, ou para devolver unha referencia ó obxecto actual a un obxecto externo (ver exemplo na paxina 11).


super é outra palabra reservada que representa unha referencia ó obxecto pai do actual, e dicir do obxecto do que deriva o obxecto actual. A súa utilidade básica é a de chamar métodos da clase base que foron sobrepostos ou a constructores da clase base. Por exemplo, podería modificarse o exemplo da páxina 11 para non necesitar o constructor de Mundo sen argumentos.


Métodos e variables de clase e métodos e variables constantes (final).

Como xa vimos, as variables e os métodos poden ser de instancia (de obxecto) ou de clase. Normalmente tanto as variables como os métodos son de instancia, e dicir, existe unha copia por cada obxecto creado desa clase. Sen embargo é posible definir variables e métodos que existen independentemente da creación de obxectos dunha clase, e que unicamente hai unha copia para tódolos obxectos da mesma. Para facer esto unicamente é necesario antepoñer na declaración a palabra reservada static.

Para invocar os métodos ou acceder ás variables static sen necesidade de crear obxectos se poñerá antes do método ou variable o nome da clase en lugar de facelo co nome do obxecto. Un exemplo de método static que forma parte da librería estándar JAVA é println, que é un método que pertence a un obxecto static (out) da clase System, e se invoca System.out.println( ... );


Outro modificador que se pode antepoñer tanto os métodos e as variables como ás clases é final:

  • Unha clase final non pode ter subclases.
  • Un método final non pode ser sobreposto por ningunha clase.
  • Unha variable final non pode cambiar o seu valor.


Herdanza


Cando facemos unha nova clase e a derivamos dunha xa existente herdamos tódalas súas características (os métodos e os datos membros). Para crear unha nova clase que derive de outra utilizaremos a palabra reservada extends (ver exemplo en páxina 5).


Sobreposición e sobrecarga de métodos


Pode ser que unha vez que temos unha clase derivada non nos convenza o xeito de funcionar dalgún método da clase base, de forma que é posible definir na clase derivada un novo método cunha definición exactamente igual que a da clase base. Esto denomínase sobreposición de métodos.

Cando nos chamemos o método da nova clase chamarase o método sobreposto, quedando oculto o da clase base. Sen embargo, é posible utilizar a palabra reservada super para acceder os métodos da clase base.


Exemplo:

class Base {


void metodo() {

System.out.println(" Clase Base..");

}

void metodo1() {

System.out.println(" Base metodo1..");

}

void metodo2() {

System.out.println(" Base metodo2 ..");

}

}


class Derivada extends Base {


void metodo() {

System.out.println(" Clase Derivada ..");

}

void metodo1() {

System.out.print(" Derivada metodo1 ->");

super.metodo1();

}

}


public class Sobreposicion {

public static void main(String[] args) {

Derivada d=new Derivada();


d.metodo();

d.metodo1();

d.metodo2();

}

}


Un caso distinto é cando dentro da mesma clase temos varias versións distintas dun mesmo método diferindo unicamente nos argumentos. Esto denomínase sobrecarga de métodos.


Constructores en JAVA (constructores por defecto)


Como xa vimos anteriormente, todas as clases dispoñen dun método especial que se invoca automaticamente ó crear un obxecto desa clase, o método constructor. O método constructor non especifica ningún valor de retorno e ten que chamarse exactamente igual que a clase e normalmente inicializa os datos do obxecto. Se nós non escribimos un constructor para a nosa clase, JAVA xerará un sen argumentos automaticamente, é o constructor por defecto.


Unha práctica moi común é sobrecargar os métodos constructores de xeito que dispoñamos de varias formas de crear obxectos dunha clase, pero para non repetir código sería bo que dende un método constructor se puidera chamar a outro. Esto faise co operador this como ilustra o exemplo da páxina 11.


Tamén resulta útil invocar directamente ó constructor da clase base dende o constructor da clase derivada a través do operador super.


O exemplo da páxina 6 amosa a orde en que JAVA vai invocando ós constructores dunha clase cando se crea un obxecto, e o gráfico da páxina 4 indica como se fai para localizar o método que se ten que invocar.


Destructores en JAVA. O recolector de lixo


JAVA libera a memoria automaticamente cando o sistema o precise mediante o chamado recolector de lixo, co que non precisa dos destructores típicos noutras linguaxes orientadas a obxecto, sen embargo unicamente libera os obxectos creados con new. Para poder realizar labores de limpeza cando os obxectos sexan destruídos se proporciona para todos os obxectos un método chamado finalize() que se invocará cando o obxecto sexa reclamado por primeira vez polo colector de lixo. O problema é que non se pode saber si un obxecto será destruído ou non.


Para limpar un obxecto, o usuario do mesmo debería crear e chamar a un método de limpeza no momento en que desexe, xa que non se pode un fiar de que se chamen realmente os métodos finalize().


JAVA dispón dos seguintes métodos para forzar a execución dos métodos finalize:


System.runFinalization(); Chama os métodos finalize dos obxectos pendentes de finalización

System.gc(); Forza a execución do recolector de lixo.

Os packages


JAVA introduce o concepto de espacio de nomes coa idea de garantir que as clases que se van a utilizar son en realidade as que queremos. Cando creamos un ficheiro fonte, este debe ter extensión .java que se transforma en un ficheiro .class por cada clase utilizada. Unha aplicación constará de moitos .class que o intérprete de JAVA terá que atopar, cargar e interpretar. As librerías son tamén un conxunto de ficheiros .class, si se quere indicar que unha serie de ficheiros .class van xuntos indícase coa palabra package o inicio do ficheiro:


package 3Drotations;

Para referirnos posteriormente a unha clase dun package é necesario indicalo con notación de puntos.


3Drotations r=new 3Drotations.Translate();


Para evitar esto é posible importar o package completo e posteriormente referirnos ás clases de forma simple (ver exemplo páxina 12):


import 3Drotations.*;


Translate r=new Translate();


É posible tamén importar unha única clase do package .. import 3Drotations.Translate;


Para que podan convivir dun xeito organizado varias versións dunha mesma clase é preciso seguir un estándar para os nomes dos packages. JAVA é unha linguaxe orientada a Internet, e polo tanto e posible que varias aplicacións utilicen versións distintas dunha clase con gran facilidade. O estándar para os nomes de packages que garante que sexan únicos é o seguinte:


  • Cada nome do package en notación de puntos corresponde cun directorio dentro da carpeta da librería JAVA
  • Debería chamarse o package co nome de dominio do autor de forma inversa, seguido pola identificación do package.


Por exemplo: gz.xavi.graficos.poligonos.


Especificadores de acceso (public, private, protected e friendly)


Como xa vimos, en JAVA os métodos e os datos membro poden ser public, private, protected ou friendly mentres que as clases poden ser unicamente public ou non public.


public : Todas as outras clases teñen acceso os métodos ou membros public.


private : Unicamente se pode acceder os métodos ou membros private desde dentro da propia clase.


protected : Unicamente se pode acceder os métodos ou membros protected desde dentro da propia clase ou desde unha clase herdada.


friendly : E o acceso por defecto e non se especifica. Unicamente se pode acceder os métodos ou membros friendly desde as clases desenvolvidas no mesmo package.






Clases abstractas e Interfaces


Un método abstracto é un método que unicamente ten definido o seu valor de retorno, nome e argumentos, non o corpo do método. Unha clase abstracta é unha clase que ten métodos abstractos. Non é posible definir obxectos de clases abstractas, se non que é necesario derivar unha nova clase e implantar o método ou métodos abstractos.

As clases abstractas utilízanse para forzar a existencia dun método dentro de clases derivadas de este, pero que a clase base non pode implantar por ser demasiado xeral. Para definir un método abstracto utilízase abstract:


abstract void calculaArea(int[] nums);


Un interface é unha clase abstracta 'pura', e dicir, non ten implantado ningún dos seus métodos. Os interfaces defínense coa palabra clave interface no lugar de class, e os datos membro que conteña serán static e final e os métodos public.


A diferencia das clases, dun interface non se poden derivar novas clases, se non que é necesario definir unha clase que o implante, e dicir, que teña definidas todos os métodos do mesmo. Para facer esto emprégase a palabra reservada implements. Unha clase pode derivarse doutra (extends) e á vez implantar outra (implements).


'Herdanza múltiple' con interfaces


JAVA é unha linguaxe de herdanza simple, o que quere dicir que unha clase unicamente pode ter unha clase base, sen embargo este límite non se da a hora de implantar interfaces. Unha clase pode estender outra e implantar varios interfaces o mesmo tempo (ou simplemente implantar varios interfaces).


Exemplo:

interface PodeVoar {

void voa();

}


interface PodeNadar {

void nada();

}


interface PodeCorrer {

void corre();

}


interface PodeFalar {

void fala(String str);

}


class Print implements PodeFalar {

public void fala(String str) {

System.out.println(str);

}

}


public class Pato extends Print implements PodeVoar, PodeNadar, PodeCorrer {

public void voa() {

fala("voando...");

}

public void nada() {

fala("nadando...");

}

public void corre() {

fala("correndo...");

}

public static void main(String[] args) {

Pato p=new Pato();

p.voa(); p.corre(); p.nada();

}

}


Contedores de obxectos e Iteradores.


Os contedores de obxectos son unha serie de clases que serven para almacenar obxectos de calquera clase e acceder posteriormente ós obxectos almacenados. A cantidade de obxectos que se poden almacenar nun container depende unicamente da memoria dispoñible. Algúns dos containers dispoñibles son: Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap ...


Da mesma forma que Arrays para as táboas temos tamén unha clase con métodos estáticos para utilizar cos containers Collections.


O principal problema dos containers é que o almacenar obxectos de tipo Object, e necesario converter posteriormente os obxectos contidos ó tipo orixinal, esto faise con casting, é dicir indicando (a clase) antes do obxecto.


Rect r= (Rect) p; // sendo p de tipo Object


Moitas veces necesitaremos ademais coñecer de qué tipo concreto é un obxecto, para elo temos o operador instanceof que devolve true si o obxecto é unha instancia da clase indicada:


if (r instanceof Rect) ....


Ficheiro:CURSO html 78c0a249.gif
En calquera clase de container debemos ter unha forma de poñer obxectos dentro do container e unha forma de sacalos. Unha forma estándar de facelo para tódolos containers é a través dun iterador. Un iterador é un obxecto que se pode mover a través de secuencias de obxectos e seleccionalos sen que haxa que preocuparse polo tipo de container no que están almacenados. Un exemplo é a Iterator, pódese obter un Iterator chamando ó método iterator() do contedor, e disporemos de funcións para eliminar obxectos ou pasar o seguinte.

Clases de containers, Iteradores e comparadores.



Excepcións


As excepcións son o tratamento de erros da linguaxe, e descenden das clases Error ou Exception que a súa vez descenden de Throwable. As clases descendentes de Error son para erros moi graves que normalmente son manexados polo propio linguaxe, o interesante reside en Exception.


A clase Exception ten as subclases en dous grupos xerais: RuntimeException, ou excepcións en tempo de execución como ArrayIndexOutofBounds, SecurityException ou NullPointerException e outras como EOFException, FileNotFoundException ou MalformedURLException. As excepcións en tempo de execución se producen normalmente por incorrección no código.


Cando se utilizan métodos que lanzan excepcións JAVA obriga a manexalas mediante a instruccións try.


Exemplo:

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

/* tratamento do erro */

System.out.println("Erro "+e.getMessage());

}


No exemplo, o método sleep pode lanzar a excepción InterruptedException, co que é necesario manexala. Con catch indicamos o que ten que facer o programa si se produce a excepción, como se pode observar a catch séguelle un parámetro que é unha variable do tipo de excepción a tratar.


Tamén é posible escribir varios bloques catch para capturar varias excepcións. Levarase a cabo o primeiro bloque que concorde coa excepción producida.


Outra instrucción que se combina con try é finally, que pode colocarse se use catch ou non. finally asegura que despois das instruccións incluídas no bloque try se leve a cabo o especificado no bloque finally, polo que é moi útil para facer limpeza antes de terminar as aplicacións (pechar ficheiros por exemplo).


Tamén é posible definir métodos que lancen excepcións utilizando a cláusula throws, o que obrigaría a usar un bloque try ó utilizar o método:


public int exemplo (int parametro) throws FileNotFoundException {

}

poden indicarse varias excepcións distintas separadas por comas. Si en un método chamamos a outro que lanza excepcións, en lugar de tratala tamén podemos 'reenviala' á función que utiliza a nosa mediante unha instrucción throw. Por suposto, é posible crear novas excepcións derivando de Throwable.


Copia de obxectos. O método clone()


Como xa vimos, non é posible copiar un obxecto dun xeito sinxelo xa que traballamos con referencias ós obxectos, non cos obxectos mesmo. Para resolver esto a clase Object ten un método chamado clone() que é o encargado de facer unha copia do obxecto actual, por desgracia unicamente traballa con unhas poucas clases como ArrayList.


A copia de obxectos non está automáticamente incluída en todas as clases, se non que é necesario implementala a través do interface Cloneable. Ademais si o obxecto é composto, cada obxecto membro ten que ser Cloneable, e ter superposto o método clone.




Exemplo:

import java.util.*;


class ObxectoClonable implements Cloneable {

int i;


ObxectoClonable (int x) { i=x; }


public Object clone() {

Object o=null;


try {

o=super.clone();

}

catch (CloneNotSupportedException e) {

System.err.println("Non soporto a copia !!");

}

return o;

}


public void setVal (int x) { i=x; }

public String toString() { return Integer.toString(i); }

}


public class Clon {

public static void main(String[] args) {

ObxectoClonable obj1= new ObxectoClonable(1);

ObxectoClonable obj2;


obj2=obj1; obj2.setVal(33);

System.out.println("Obj1="+obj1);

System.out.println("Obj2="+obj2);

obj1.setVal(11); obj2=(ObxectoClonable) obj1.clone();

System.out.println("Obj1="+obj1);

System.out.println("Obj2="+obj2);

obj2.setVal(99);

System.out.println("Obj1="+obj1);

System.out.println("Obj2="+obj2);

}

}

Ficheiros


En JAVA a información pódese almacenar e recuperar mediante un sistema de comunicación chamado fluxos implantados no paquete java.io. Os fluxos poden dividirse en fluxos de bytes no que se manexan bytes, números enteiros e tipos simples de datos, e de carácteres, no que se manexan arquivos de texto e outras fontes de carácteres. En JAVA, debido ó soporte Unicode calquera clase de datos que teña texto debería ser tratado cun fluxo de carácteres. As clases utilizadas para o acceso son:


File

Exemplo: import java.io.*;


public class FileList extends File {

String[] flist;


FileList(String path) {

super(path);

flist=list();

for (int i=0;i<flist.length;i++) {

if (new File(flist[i]).isDirectory())

System.out.println(flist[i]+"\t <DIR>");

else

System.out.println(flist[i]);

}


}


public static void main(String[] args) {

new FileList(args[0]);

}

}

FileInputStream (fluxo de bytes) FileOutputStream (fluxo de bytes)

FileReader (fluxo de carácteres) FileWriter (fluxo de caracteres)

RandomAccessFile (acceso aleatorio)


Exemplo:

import java.io.*;


public class Lee {


Lee(String fich) {

int eof=0;

try {

FileReader f=new FileReader(fich);

System.out.println("Listando "+fich);

eof=f.read();

while (eof!=-1) {

System.out.print((char)eof);

eof=f.read();

}

f.close();

} catch (FileNotFoundException e) {

System.out.println("Ficheiro non atopado!!");

}

catch (IOException e) {

System.out.println("Erro o abrir o Ficheiro!!");

}

}


public static void main(String[] args) {

new Lee(args[0]);

}

}


E dispoñemos das seguintes clases para facer filtrado


BufferedInputStream BufferedOutputStream

BufferedReader BufferedWriter

DataInputStream DataOutputStream


O filtrado consiste en engadirlle novas características a un fluxo de datos que foi aberto mediante unha operacion File*. Por exemplo, Buffered* proporciona acceso a través dun buffer en memoria e Data* proporciona a posibilidade de acceder os datos coma si foran tipos primitivos JAVA en lugar de facelo como bytes ou caracteres. Poden combinarse varios filtrados en cascada.


Exemplo: import java.io.*;


public class Lee {

Lee(String fich) {

int eof=0;

try {

FileReader f=new FileReader(fich);

BufferedReader f1=new BufferedReader(f);

System.out.println("Listando "+fich);

eof=f1.read();

while (eof!=-1) {

System.out.print((char)eof);

eof=f1.read();

}

f1.close();

} catch (FileNotFoundException e) {

System.out.println("Ficheiro non atopado!!");

} catch (IOException e) {

System.out.println("Erro o abrir o Ficheiro!!");

}

}


public static void main(String[] args) {

new Lee(args[0]);

}

}


Threads

Os threads ou fíos de execución indican que unha tarefa ten funcionando 'simultaneamente' diversas partes. A forma máis sinxela de crear threads é derivando da clase Thread:


Exemplo:

public class Tarefa extends Thread {

private int id;


Tarefa(int n) {

id=n;

System.out.println("Creando Tarefa "+id);

}


public void run () {

int i=0;

while(true) {

System.out.println("Tarefa "+id+" = "+i++);

}

}


public static void main(String[] args) {

new Tarefa(0).start();

new Tarefa(1).start();

new Tarefa(2).start();

new Tarefa(3).start();

}


}


Unha forma alternativa de facelo é implementando a interface Runnable, e posteriormente crear un obxecto Thread e chamar ó método start();


Exemplo:

public class Tarefa implements Runnable {

private int id;


Tarefa(int n) {

id=n;

System.out.println("Creando Tarefa "+id);

new Thread(this).start();

}


public void run () {

int i=0;

while(true) {

System.out.println("Tarefa "+id+" = "+i++);

}

}


public static void main(String args[]) {

new Tarefa(0);

new Tarefa(1);

new Tarefa(2);

new Tarefa(3);

}


}


CREACIÓN DE APPLETS


JAVA debe a meirande parte da súa popularidade a posibilidade de incluír aplicacións dentro dunha páxina WEB. A maior parte dos navegadores WEB dispoñen dunha máquina virtual JAVA que é capaz de interpretar e executar byte-code JAVA. O código JAVA incluído dentro dunha páxina WEB denomínase applet.


Como os applets execútanse dentro do sistema do usuario existen unha serie de restriccións de seguridade sobre o que un applet JAVA pode facer dentro do sistema do usuario:


  • Un applet non pode ler nin escribir arquivos no sistema do usuario
  • Un applet non pode comunicarse cun sitio de Internet distinto do que serviu a páxina WEB que incluía o applet.
  • As ventás despregadas por un applet amosan sempre unha mensaxe 'Ventá de applet de JAVA'.


Sen embargo as máquinas virtuais dos navegadores poden permitir configurar niveis de seguridade habilitando algunha ou todas as características que non funcionan normalmente nos applets. Outra posibilidade é o uso de firmas dixitais para habilitar ós applets unha funcionalidade completa.


Unha firma dixital é un arquivo encriptado que acompaña a un programa, indicando de quen ven (certificado). O proceso para firmar un applet é o seguinte:


  1. Debemos xerar dous arquivos encriptados coa utilidade keytool do JDK, un que se denomina clave pública e o outro clave privada. A clave privada almacénase de forma que ninguén poda acceder a ela, e a clave pública faise dispoñible a todo o mundo.
  2. Necesitamos unha empresa imparcial que verifique a nosa identidade (por exemplo VeriSign ou Thawte Certification). Mandámoslle a nosa clave pública e un arquivo descritivo da nosa compañía.
  3. A compañía de seguridade comproba que a nosa compañía é quen di ser, crea un novo arquivo encriptado, o certificado e nolo envía.
  4. Mediante a utilidade jar e clave privada e o certificado firmamos o applet.
  5. Cando descarguen a páxina WEB co applet daremos instruccións para descargar a clave pública.


A necesidade da clave pública e privada radica en que ninguén poda firmar applets usando o noso certificado. A clave pública sirve para que a compañía certificadora verifique a nosa identidade e para comprobar o certificado, e a clave privada utilízase para certificar os applets.


O usuario do applet atopará a seguinte situación:


  1. Recíbese un aviso de que non se pode executar o applet sen establecer a nosa compañía como fiable, e se descarga a clave pública.
  2. Unha vez que se decide confiar no applet é posible utilizar a ferramenta do JDK jarsigner para agregar a compañía a unha lista de compañías fiables.


Sen embargo, hoxe en día o único modo de firmar un applet de forma efectiva é seguindo os procedementos establecidos pola compañía a que pertence o navegador, si se quere poder utilizar varios navegadores é necesario firmar o applet para todos eles.







Vexamos un exemplo de applet:


import java.applet.*;

import java.awt.*;


public class HolaMundo extends Applet {

public void paint (Graphics g) {

g.drawString("Hola Mundo !!!");

}

}

Unha vez compilado será posible incluílo dentro dunha páxina WEB do seguinte xeito:


<HTML>

<HEAD>

<TITLE>

Applet HolaMundo

</TITLE>

<BODY>

Saludando...
<APPLET CODE="HolaMundo.class" WIDTH=100 HEIGHT=30> O sinto, non soporto JAVA </APPLET> </BODY> </HTML>
Os atributos que pode levar a etiqueta APPLET son case iguais os da etiqueta <IMG>:
ALIGN que pode ser LEFT, RIGHT, TOP, ABSMIDDLE, MIDDLE, BASELINE, ABSBOTTOM HSPACE, VSPACE CODEBASE indica a carpeta ou o sitio WEB onde se atopan os arquivos que compoñen o applet.
Como cando compilamos unha aplicación (ou applet) JAVA xera un ficheiro .class por cada clase utilizada, sería moi útil poder compactar todos eses arquivos en un so, de forma que se axilizara a descarga. Esto conséguese coa utilidade jar. jar comprime os arquivos que nós lle indiquemos en un so con extensión .jar. Para cargar o applet completo é necesario utilizar unha etiqueta ARCHIVE="nome.jar". Tamén é posible pasarlle parámetros o applet engadindo etiquetas ... <PARAM NAME=nome VALUE="valor">
A clase Applet.
Como xa vimos anteriormente, para crear un applet é necesario derivar unha clase da clase Applet. Os métodos máis importantes da clase Applet e que teremos que sobrepoñer habitualmente son os seguintes:
INICIACIÓN public void init() { } A iniciación prodúcese únicamente cando se carga o applet. Aquí poderá incluirse o código que queremos que se leve a cabo no momento de cargar o applet.
ARRANQUE public void start() { } O arranque prodúcese despois de ser iniciado o applet, e ademais cando o applet volva a funcionar despois de estar parado. Si nos cambiamos de páxina o applet detense, pero o regresar non se volve a cargar. Normalmente poñeremos neste método o código que marque o comportamento de inicio do applet.

PARADA public void stop() { } Un applet pode deterse cando nos cambiemos a outra páxina, ou cando chamemos directamente o método stop. Normalmente, si temos un applet con varios fíos de execución estos continúan funcionando aínda que deixemos a páxina, con este método poden deterse.
DESTRUCCION public void destroy() { } Este método chámase cando o applet teña que ser liberado de memoria ou o pechar o navegador.
PINTADO public void paint (Graphics g) { } A función paint invócase sempre que o applet precise ser dibuxado de novo. E polo tanto o sitio ideal para dibuxar na pantalla.
Exemplo: import java.applet.*; import java.awt.*;
public class HolaMundo extends Applet { Font f; Color c; String s; int r; int g; int b;
public void init() { String fonte; String size; String col;
s=getParameter("TEXT"); if (s==null) s="¡ Sin Texto !"; fonte=getParameter("FONT"); if (fonte==null) fonte="Courier"; size=getParameter("SIZE"); if (size==null) size="12"; f=new Font(fonte,Font.BOLD,Integer.parseInt(size)); col=getParameter("RED"); if (col!=null) r=Integer.parseInt(col); col=getParameter("GREEN"); if (col!=null) g=Integer.parseInt(col); col=getParameter("BLUE"); if (col!=null) b=Integer.parseInt(col); c=new Color(r,g,b); }
public void paint (Graphics g) { g.setFont(f); g.setColor(c); g.drawString(s,50,80); } }
Para comprobar o correcto funcionamento dun applet é necesario escribir unha páxina WEB que inclúa o mesmo e posteriormente abrila cun navegador. Outra posibilidade é utilizar a ferramenta do JDK appletviewer, que visualizará únicamente o applet.
As veces os navegadores non teñen todas as características da última versión de JAVA, co que certos applets non funcionarán. Para probar os mesmos é imprescindible o uso de appletviewer, aínda que normalmente é posible actualizar a máquina virtual JAVA do navegador. O método getAppletContext() da clase Applet devolve un obxecto que permite controlar algúns aspectos do navegador, por exemplo o método showStatus() amosa mensaxes na liña de estado ou showDocument() amosa unha nova páxina.
PROGRAMACIÓN DE G.U.I.
Hoxe en día practicamente todas as aplicacións se comunican co usuario a través dunha interface gráfica (Graphic User Interface). JAVA dispón dunha completísima librería de clases para implantar GUIs, tanto en aplicacións como en applets.
Unha posibilidade interesante a hora de desenvolver aplicacións en JAVA é a posibilidade de que podan funcionar como aplicacións independentes ou como applets según se desexe, xa que as clases que se utilizan para desenvolver o interface son as mesmas.
Podemos distinguir dous grupos de clases enfocadas a GUI, a máis básica (AWT) que está soportada por case todos os navegadores e a máis moderna (SWING) derivada en gran parte de AWT, que aínda que é moito máis atractiva non está soportada.
AWT
AWT consta dunha serie de clases que facilitan a creación de GUIs e recibir a entrada do usuario desde o rato e o teclado. AWT proporcionará unha interfaz que terá unha aparencia similar sexa cal sexa o sistema no que se execute. Un interfaz de usuario con AWT consta de:

  • Compoñentes: As compoñentes son calquera cousa que se poda poñer na interface do usuario, coma botóns, listas, campos de edición, cadros de verificación, etc.
  • Contedores: Os contedores son compoñentes capaces de albergar outros compoñentes, un exemplo de contedor a clase Applet.
  • Administradores de deseño: E un compoñente que non ten visualización, pero que define a forma de organizarse dos compoñentes dento do seu contedor.


Os compoñentes formarán parte da interfaz de usuario cando os agreguemos a un contedor, un contedor é a súa vez un compoñente de forma que pode ser agregado a outro contedor. Os contedores teñen un método add que serve para engadir compoñentes:
Exemplo: import java.applet.*; import java.awt.*;
public class Conten extends Applet { Button btn=new Button(" Este e un boton insertado nun applet ");
public void init() { add(btn); } }
Uso de Imaxes e son Para manexar imaxes .GIF e .JPG en JAVA utilízase a clase Image. E posible cargar unha imaxe dende o sistema de ficheiros local ou desde un servidor WEB representando a súa dirección como un obxecto de tipo URL e chamado ó método de Applet getImage, e posteriormente debuxalo chamando o método drawImage de Graphics.
Exemplo: import java.awt.Image; import java.awt.Graphics;
public class ImageLoader extends java.applet.Applet { Image img;
public void init() { img=getImage(getCodeBase(),"Debuxos/mus.jpg"); }
public void paint(Graphics screen) { screen.drawImage(img,10,10,this); } }
<HTML> <HEAD><TITLE> Proba de Applets </TITLE></HEAD> <BODY><APPLET CODE="ImageLoader.class" WIDTH=380 HEIGHT=400></APPLET></BODY> </HTML>
Exemplo: import java.awt.*; import java.awt.image.*; import java.net.URL;
class Img {
public static Image getDemoImage(String fileName, Component cmp) { URL url = Img.class.getResource(fileName); Image img = cmp.getToolkit().createImage(url); return img; } }
public class DemoImages extends Frame { Image imx;
DemoImages() { setTitle("Visor de Imaxes"); setSize(380,450); setVisible(true); imx=Img.getDemoImage("Debuxos/mus.jpg",this); }
public void paint(Graphics scr) { scr.drawImage(imx,10,30,this); }
public static void main(String[] args) { new DemoImages(); } }
Exemplo: import java.awt.*; import java.awt.image.*; import java.net.URL;
class Img {
public Image getDemoImage(String fileName, Component cmp) { URL url = this.getClass().getResource(fileName); Image img = cmp.getToolkit().createImage(url); return img; } }
public class DemoImages extends Frame { Image imx;
DemoImages() { Img load=new Img();
setTitle("Visor de Imaxes"); setSize(100,130); setVisible(true); imx=load.getDemoImage("Debuxos/basket.gif ",this); }
public void paint(Graphics scr) { scr.drawImage(imx,10,30,this); }
public static void main(String[] args) { new DemoImages(); } }
Como podemos observar nos dous últimos exemplos, no caso das aplicacións é necesario cargar o url como un recurso, (getResource) e logo crear a imaxe utilizando o obxecto da clase Toolkit asociada cos compoñentes que proporciona unha serie de utilidades de manexo gráfico e que se obtén co método getToolkit(). Pode verse tamén que class pode utilizarse como unha 'abreviatura' do método getClass() de Object que devolve o seu obxecto Class asociado. En canto ó son, JAVA manexa os formatos AU, AIFF, WAV e algúns tipos de MIDI a través do método play de Applet. Tamén se pode almacenar e manexar un son mediante obxecto de tipo AudioClip creado co método getAudioClip de Applet. As aplicacións poderán tamén chamar o método static Applet.newAudioClip.
Animación en JAVA Como xa vimos anteriormente, cando é necesario repintar unha ventá invócase automaticamente ós métodos paint dos compoñentes. E posible solicitar ó sistema o repintado invocando o método repaint. Para realizar unha animación bastará con debuxar o que se queira, chamar a repaint, debuxar o cadro seguinte e volver a chamar a repaint e así sucesivamente. Cando nos facemos unha animación o sistema estará ocupado facendo os debuxos e non poderá facer outra cousa, como por exemplo atender as entradas do usuario, para solucionar este problema pódense empregar Threads.
Si o que estamos a desenvolver é un applet temos que ter en conta que o cambiar de páxina o applet se para, pero os procesos asociados non. É necesario sobrepoñer o método start para lanzar o thread de forma que arranque de novo ó regresar á páxina e stop para parar o thread o abandonar a mesma.
Exemplo: import java.util.*; import java.awt.*;
class DigitalClock extends Frame implements Runnable { Date date;
DigitalClock() { setTitle("Reloxo Dixital"); setSize(250,100); new Thread(this).start(); setVisible(true); }
public void paint (Graphics g) { date=new Date(); g.setColor(Color.blue); g.drawString(" "+date.toString(),10,50); }
public void run() { while(true) { repaint(); try { Thread.sleep(1000); } catch(InterruptedException e) {} } } }
public class Clock { public static void main(String[] args) { int i=0;
System.out.println("Funciona o reloxo "); System.out.println(" E a vez vou contando"); new DigitalClock(); while(true) System.out.println("Conta: "+i++); } }
Observando a execución do exemplo anterior podemos observar un certo parpadeo na ventá, esto é producido porque en lugar de chamar o método paint a máquina JAVA chama a un método chamado update que se encarga de borrar o contexto gráfico e chamar a paint.
Unha forma de evitar o parpadeo sería sobrepoñer o método update de forma que non limpara a pantalla, o que en moitos casos serviría. Para outros casos a mellor solución é a utilización de dobre buffer. A técnica de dobre buffer consiste en crear unha imaxe non visible que é de onde nos debuxamos, cando o debuxo está terminado o copiamos na pantalla.
Para facer esto precisamos crear un obxecto da clase Image do mesmo tamaño que a pantalla de debuxo (información que obtemos co método getSize ou size da clase Component.) mediante o método createImage. Posteriormente podemos obter o contexto gráfico do obxecto Image mediante getGraphics que utilizaremos logo no método paint para facer o debuxo e copialo á pantalla con drawImage. Precisaremos tamén sobrescribir o método update para evitar o borrado continuo da pantalla e o método destroy para liberar con dispose o dobre buffer.
A clase Graphics Un obxecto de tipo Graphics almacena toda a información necesaria para realizar as distintas operacións de debuxo que soporta JAVA, e polo tanto se utiliza para debuxar. Podemos obter o obxecto Graphics dun compoñente chamado o seu método getGraphics().
Exemplo: import java.awt.*;
public class Hola extends Frame { Graphics g;
Hola () { setTitle("Aplicacion Grafica"); setSize(200,100); show(); }

void print(String str) { g=getGraphics(); g.drawString(str,50,50);
}
public static void main(String args[]) { Hola h=new Hola(); h.print("Hola, funciona ?"); } }
A clase Font A clase Font permite variar a fonte con que se escribe texto no interior dun contedor utilizando o método setFont.
A clase Color A clase Color permite variar o color co que se debuxa mediante o método setColor.
A clase Label A clase Label é un compoñente que representa unha etiqueta de texto.
A clase Button A clase Button permite a creacións de botóns (ver exemplo en páxina 31).
A clase Checkbox e CheckboxGroup Mediante Checkbox é posible crear cadros de verificación, que son cadros que poden estar seleccionadas ou non. Polo común utilízanse para seleccionar o u deseleccionar opcións nas aplicacións. Os cadros de verificación poden ser agrupadas de forma que unicamente se poda seleccionar unha do grupo mediante a clase CheckboxGroup combinada co constructor axeitado de Checkbox.
A clase Choice A clase Choice implementa listas de selección que permiten elixir un elemento entre varios dunha lista despregable. A clase TextField A clase TextField utilízase para crear campos de edición de texto.
A clase TextArea TextArea proporciona a posibilidade de crear áreas de texto editables que manexan máis de unha liña de entrada.
A clase List A clase List permite a creación dunha lista na que é posible seleccionar elementos.
A clase Scrollbar A clase ScrollBar permite incluír barras de desprazamento, tanto verticais como horizontais.
A clase Canvas A clase Canvas utilízase para crear lenzos. Un lenzo son compoñentes que se poden utilizar para despregar imaxes ou animación.
A clase Frame A clase Frame sirve para crear unha ventá. Pódese obter o Frame que contén un applet mediante o método getParent.
A clase Dialog A clase Dialog e moi similar a Frame, pero están pensados para ventanas para 'dialogar' co usuario un instante (mostrar avisos, pedir información específica ..). Un diálogo modal impide a entrada a calqueira outra ventá mentras non se peche. Proporciónanse dúas clases Dialog e FileDialog.
Exemplo: import java.awt.*;
public class Exemplo extends Frame {
Exemplo () { setSize(200,100); show(); }
public static void main(String args[]) { FileDialog fd=new FileDialog(new Exemplo(),"File Dialog"); fd.show(); } }
A clase Panel Panel é o contedor máis sinxelo que temos. Utilízase para agregar e organizar compoñentes en un Frame, Dialog ou Applet.
Exemplo: import java.awt.*;
class Escribe extends Panel { String texto; int x; int y;
Escribe() { }
public void escribe(String t, int px, int py) { texto=t; x=px; y=py; }
public void paint(Graphics g) { if (texto!=null) { g.drawString(texto,x,y); } } } public class Hola extends Frame { Escribe pizarra=new Escribe();
Hola () { setTitle("Aplicacion Grafica"); setSize(200,100); add(pizarra); show(); }
public void escribe(String s, int x, int y) { pizarra.escribe(s,x,y); }
public static void main(String args[]) { Hola h=new Hola(); h.escribe("Hola Mundo",65,40); } } As clases Menu, MenuBar e MenuItem Mediante a clase MenuBar é posible crear un menú de ventá, que se agrega co método setMenuBar de Frame. Posteriormente é posible crear elementos de menú individuais coa clase Menu e engadilos ó MenuBar co seu método add. Para engadir subelementos a cada elemento da barra de menú empregaremos a clase MenuItem.
O crear un elemento de menú coa etiqueta '-', crearáse un separador. Tamén temos unha clase CheckboxMenuItem, que é un elemento de menú cunha caixa de verificación.
Administradores de Deseño
Como xa comprobamos nos exemplos anteriores aparentemente non temos control sobre a posición que ocupan os diferentes compoñentes no seu contedor. Para controlar a posición dos distintos compoñentes é necesario utilizar os administradores de deseño, esto é así debido a que JAVA pode ser executado en moitas plataformas distinta e é preciso un método para que a aparencia das aplicacións sexa similar en todas elas.
AWT ten cinco administradores de deseño básicos: FlowLayout, GridLayout, BorderLayout, CardLayout e GridBagLayout. Para incluír un administrador de deseño nun contedor é preciso utilizar o método setLayout.
FlowLayout FlowLayout organiza os compoñentes un tras outro, do mesmo xeito que as palabras nunha páxina.
GridLayout Este administrador coloca os compoñentes dentro dunha cuadrícula de renglóns e columnas, que poden estar separadas por uns marxes horizontais e verticais que é posible especificar no constructor. BorderLayout Divide o contedor en cinco seccións: "North", "South", "East", "West" e "Center". Con este deseño, os compoñentes dos catro puntos cardinais ocupan todo o que necesiten e todo o que sobra o ocupa o centro. Os compoñentes engadiranse con add(seccíón,compoñente);
CardLayout O administrador CardLayout oculta algúns compoñentes da vista, en realidade o que fai e almacenar unha serie de contedores uns enriba doutros amosando unicamente o contedor que se seleccione mediante o método show (Object , String ); de CardLayout. Para utilizar este administrador é necesario ir engadindo os compoñentes dándolles un nome mediante o método add(String, Component);
GridBagLayout Este administrador de deseño é o máis complexo pero o máis completo de todos, e da mesma forma que GridLayout divide o espacio en cuadrículas de renglóns, sen embargo cando utilizamos un administrador GridBagLayout....

  • Un compoñente pode ocupar máis dunha celda na cuadrícula
  • As proporcións entre renglóns e columnas distintas non teñen que ser iguais.
  • Os compoñentes dentro das celdas de cuadrícula poden organizarse de distintas maneiras.


A forma de utilizar este administrador consiste en utilizar a clase GridBagLayout e GridBagConstraints da forma seguinte:

  1. Crear un obxecto GridBagLayout e poñelo como administrador de deseño.
  2. Crear un obxecto GridBagConstraints
  3. Establecer as propiedades de GridBagConstraints
  4. Asignar esas propiedades ó compoñente que vai a ser agregado co método setConstraints do administrador de deseño (GridBagLayout).
  5. Agregar o compoñente con add()


As propiedades principais que asignaremos ó compoñente serán a columna e o renglón onde irá situado (gridx, gridy), o número de celdas que ocupará o compoñente en sentido horizontal e vertical (gridwidth e gridheight), e o tamaño que vai a ter en porcentaxe sobre o total (weightx, weighty). Outras propiedades son fill e anchor. fill indica como fai o compoñente para ocupar a celda e anchor funciona de forma parecida o administrador BorderLayout.

Ademais dos administradores de deseño é posible indicar tamén a marxe que se desexa conservar dentro de cada contedor ó agregar compoñentes mediante a clase Insets. Para especificar unha marxe bastará con sobrescribir o método do contedor public Insets insets(); ou public Insets getInsets(); creando e devolvendo un novo obxecto Insets.
SWING
O paquete swing de javax (javax.swing.*) e unha evidente mellora do AWT proporcionando ós compoñentes un aspecto personalizable. Todos os compoñentes de swing son subclases de JComponent, e normalmente se chaman da mesma forma que os de AWT pero con J diante. Os obxectos JFrame a diferencia de Frame non se lle poden engadir compoñentes directamente, se non que temos que obter o seu Container previamente mediante o método getContentPane(), ou agregar un novo panel con setContentPane.
Exemplo: import java.awt.*; import javax.swing.*;
class Escribe extends JPanel {
public void paint(Graphics scr) { scr.drawString("Hola Mundo",10,10); } }
public class Hola extends JFrame { Escribe p=new Escribe();
Hola () { setContentPane(p); setVisible(true); }
public static void main(String args[]) { new Hola(); } } Exemplo: import java.awt.*; import javax.swing.*;
public class DemoImages extends JFrame { Icon imx=new ImageIcon("Debuxos/basket.gif");
DemoImages() {

setTitle("Visor de Imaxes"); setSize(100,100); getContentPane().add(new JButton(imx)); setVisible(true); }
public static void main(String[] args) { new DemoImages(); } }
Outras clase interesante e JOptionPane, que proporciona unha serie de diálogos estándar.
EXEMPLO: import javax.swing.*;
public class Mensaxe { public static void main (String[] args) { JOptionPane.showMessageDialog(null,args[0]); System.exit(0); } }
Métodos interesantes dos compoñentes SWING é setToolTipText(String), que permite visualizar unha ventá emerxente con información, e setMnemonic, que permite a implantación de teclas aceleradoras.
Manexo de eventos
De vital importancia en calquera aplicación é o manexo da entrada do usuario no programa. JAVA proporciona unha serie de mecanismos para facelo. Xeralmente podemos distinguir entre os seguintes eventos:

  • Clicks do rato
  • Movimentos do rato
  • Teclas pulsadas
  • Eventos da interfaz (movimento de barras de desplazamento, activación de menús...)


Para manexar estos eventos antiguamente se utilizaba un método de Component chamado handleEvent que posteriormente chama a un manexador máis específico como mouseDown, mouseUp, mouseMove, mouseEnter, mouseExit, mouseDrag, keyDown ou keyUp.... Para os eventos da interfaz se utilizaba action, gotFocus, lostFocus.
Sen embargo, o novo modelo de eventos require que si unha clase desexa respostar a un evento ten que implementar a interfaz de manexo de eventos. Estas interfaces chámanse escoitadores de eventos e pode ser un dos seguintes:
ActionListener (Eventos da interfaz) AdjustmentListener (Eventos de axuste como mover unha barra de desplazamento) FocusListener (Eventos de foco) ItemListener (Eventos o cambiar elementos como casillas de verificación) KeyListener (Eventos de teclado) MouseListener (Eventos do raton) MouseMotionListener (Eventos de movimento do rato) WindowListener (Eventos de ventana como aumentar, disminuir ou pechar) Estas interfaces dispoñen de clases abstractas relacionadas para facilitar a creación de escoitadores de eventos, como MouseAdapter, WindowAdapter ou FocusAdapter, e para utilizalos é necesario asociar a clase de manexo dos eventos co compoñente que os produce mediante o método axeitado:
addActionListener() addFocusListener() addKeyListener() addAdjustmentListener() addItemListener() addMouseListener() addMouseMotionListener() addWindowListener() EXEMPLO
Ficheiro AplicacionGrafica.java
import java.awt.*; import java.awt.event.*;
class CloseApp extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } }
public class AplicacionGrafica extends Frame { private Pinta dibujo;
public AplicacionGrafica() { setLayout(new BorderLayout(20,20)); dibujo=new Pinta(); add(dibujo); setSize(400,200); setBackground(Color.blue); show(); }
public AplicacionGrafica(String titulo) { this(); setTitle(titulo); }
public static void main(String args[]) { AplicacionGrafica ag=new AplicacionGrafica(" P I N T A "); ag.addWindowListener(new CloseApp()); } }

Ficheiro Pinta.java
import java.awt.*; import java.awt.event.*; import java.util.Vector;
class Pinta extends Canvas {
private Vector lineas; private int lastX; private int lastY;
public Pinta() { setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); lineas = new Vector(100,50); addMouseListener(new ML()); addMouseMotionListener(new MA()); }
class ML extends MouseAdapter { public void mousePressed(MouseEvent e) { lastX = e.getX(); lastY = e.getY(); } }; class MA extends MouseMotionAdapter { public void mouseDragged(MouseEvent e) { Graphics g=getGraphics();
g.setColor(Color.white); g.drawLine(lastX,lastY,e.getX(),e.getY()); lineas.addElement(new Segmento(lastX,lastY,e.getX(),e.getY())); lastX = e.getX(); lastY = e.getY(); repaint(); } };
public void paint(Graphics g) { Segmento s; int i;
g.setColor(Color.red); for(i = 0; i<lineas.size(); i++) { s = (Segmento)lineas.elementAt(i); g.drawLine(s.getStartX(),s.getStartY(),s.getEndX(),s.getEndY()); } } }
class Segmento {
private Point origen; private Point fin;
public Segmento(int startX, int startY, int endX, int endY) { origen = new Point(startX,startY); fin = new Point(endX,endY); }
public int getStartX() { return origen.x; }
public int getStartY() { return origen.y; }
public int getEndX() { return fin.x; }
public int getEndY() { return fin.y; } }
Acceso a bases de datos
Para acceder a unha base de datos é necesario crear un String seleccionando a base de datos que especifique:

  1. Que estamos a utilizar JDBC con "jdbc"
  2. O subprotocolo, que é o nome do driver ou o nome do mecanismo de conexión a base de datos, por exemplo "odbc"
  3. O identificador da base de datos. O identificador varía dependendo do driver utilizado, pero normalmente é un nome lóxico que o sistema relaciona coa base de datos física.


Unha vez creado o String que selecciona a base de datos e o método de conexión é necesario seleccionar o driver a utilizar mediante o método forName de Class e establecer a conexión co método estático getConnection da clase DriverManager que nos devolverá un obxecto Connection. Mediante o obxecto Connection poderá crearse un obxecto Statement que se poderá utilizar para facer peticións SQL á base de datos. Estas peticións devolverán un obxecto ResultSet almacenando os resultados da sentencia SQL invocada.
EXEMPLO: import java.sql.*;
public class Look { public static void main(String[] args) throws SQLException, ClassNotFoundException { String dbUrl="jdbc:odbc:PruebaJDBC"; String user=""; String password="";
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Connection c=DriverManager.getConnection(dbUrl,user,password); Statement s=c.createStatement(); ResultSet r=s.executeQuery("SELECT * FROM Empleados"); while(r.next()) { System.out.println(r.getString("IdEmpleado")+ ", "+r.getString("Nombre_")); } s.close(); } }
Comunicacións
JAVA proporciona un conxunto de clases para establecer conexións entre os applets e as aplicacións a outro sistema a través da rede implementadas no package java.net. Convén recordar que os applets JAVA non poden ler nin escribir no disco da máquina na que están funcionando, nin se poden conectar a sistemas distintos dos que serviron o applet.
Existen dous formas sinxelas de comunicarse con outros sistemas:

  • Mediante o método getInputStream()
  • Mediante as clases Socket e ServerSocket


getInputStream utilízase para establecer conexións mediante un obxecto URL a un servidor e ler datos. Para cargar un documento de texto e leelo liña por liña é necesario seguir os seguintes pasos:

  1. Crear o obxecto URL que representa a dirección WEB do recurso.
  2. Crear un obxecto URLConnection e establecer unha conexión co URL
  3. Utilizar o método getInputStream() de URLConnection pra crear un InputStreamReader que poda ler un fluxo de datos do URL.
  4. Enlazar cun BufferedReader para mellorar o acceso.


Exemplo:

import java.net.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*;
class CloseApp extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } }
public class Leeweb extends JFrame implements Runnable { URL paxweb; TextArea box=new TextArea("Lendo paxina....");
public Leeweb(String url) { setTitle("Exemplo de conexión cun servidor WEB"); setLocation(190,90); setSize(600,500); getContentPane().add(box); try { paxweb=new URL(url); } catch (MalformedURLException e) { JOptionPane.showMessageDialog(this,"A páxina "+url+" non existe"); System.exit(0); } }
public void run() { URLConnection conn=null; InputStreamReader in; BufferedReader data; String line; StringBuffer buf=new StringBuffer();
try { conn=paxweb.openConnection(); conn.connect(); box.setText("Conexion establecida...."); in=new InputStreamReader(conn.getInputStream()); data=new BufferedReader(in); box.setText("Recibindo datos...."); while((line=data.readLine())!=null) { buf.append(line+"\n"); } box.setText(buf.toString()); } catch (IOException e) { JOptionPane.showMessageDialog(this,"IO Error: "+e.getMessage()); System.exit(0); } }
public static void main(String[] args) { Leeweb w=new Leeweb(args[0]);
w.addWindowListener(new CloseApp()); w.setVisible(true); new Thread(w).start(); } }








Socket e ServerSocket proporcionan acceso ás técnicas de programación sobre TCP e UDP. A clase Socket proporciona unha interfaz de socket para cliente, similar os sockets estándar de UNIX. Para abrir unha conexión é necesario crear unha nova instancia de Socket(NomeHost, Porto) e posteriormente utilizar os fluxos de entrada e de saída para ler ou escribir no mesmo :
Socket conexion=new Socket(NomeHost, Porto); BufferedInputStream datain=new BufferedInputStream(conexion.getInputStream()); BufferedOutputStream dataout=new BufferedOutputStream(conexion.getOutputStream()); DataInputStream in=new DataInputStream(datain); DataOutputStream out=new DataOutputStream(dataout);

Unha vez que se rematóu de traballar co socket é necesario pechalo con conexion.close();
Os sockets do lado servidor traballan dun modo similar. Un socket servidor 'escoita' nun porto TCP esperando por unha petición de conexión por parte dun cliente, e mediante o método accept() pode aceptar a mesma.
Para crear un socket de servidor e asocialo a un porto é necesario crear un novo obxecto ServerSocket: ServerSocket escoita=new ServerSocket(38910); e para escoitar no porto e aceptar novas conexións utilízase o método accept: escoita.accept(); unha vez feito esto pódense utilizar fluxos de entrada e saída para comunicarse co cliente.
O seguinte exemplo consta dun servidor que acepta conexións de múltiples clientes e permite manter comunicación escrita con eles:
Ficheiro ChatServer.java (servidor)
import java.net.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*;
public class ChatServer implements Runnable { ServerSocket socket;
public ChatServer() { try { socket=new ServerSocket(17800); } catch(IOException e) { JOptionPane.showMessageDialog(null,"Imposible crear socket"); System.exit(0); } }
public void run() { Socket client=null; String nome="JEJEJE"; StreamSocket sk;
try { while(true) { try { client=socket.accept(); sk=new StreamSocket(client); nome=sk.getSocketReader().readLine(); new ChatFrame(sk,nome,true); } catch(IOException e) { JOptionPane.showMessageDialog(null,"Cliente rexeitado"); } } } finally { JOptionPane.showMessageDialog(null,"Saindo do Server"); try { socket.close(); } catch(IOException e) { JOptionPane.showMessageDialog(null,"Erro Pechando server"); } } } public static void main(String[] args) { ChatServer cs=new ChatServer();
new Thread(cs).start(); } }
Ficheiro ChatClient.java (cliente)
import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.net.*; import java.io.*;
public class ChatClient implements Runnable { Socket socket; StreamSocket sk; String nome; PrintWriter pwr;
public void run() { sk=new StreamSocket(socket); pwr=sk.getSocketWriter(); pwr.println(nome); pwr.flush(); new ChatFrame(sk,false); }
ChatClient(String ip,String port) { try { socket=new Socket(InetAddress.getByName(ip),Integer.parseInt(port)); new Thread(this).start(); } catch(IOException e) { JOptionPane.showMessageDialog(null,"Imposible conectar"); System.exit(0); } }
public static void main(String[] args) { new ChatClient(args[1],args[2]).nome=args[0]; } }
Ficheiro ChatFrame.java (Clase auxiliar)
import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.net.*; import java.io.*;
class ChatFrame extends JFrame implements Runnable { TextArea box=new TextArea(); JButton btn=new JButton("Enviar"); JTextField txt=new JTextField(); BufferedReader brdr; PrintWriter pwr; Socket socket; boolean server; boolean loop=true; String user;
class SendData implements ActionListener { public void actionPerformed(ActionEvent e) { String texto=txt.getText()+"\n"; box.append(texto); txt.setText(""); pwr.print(texto); pwr.flush(); } }


class CloseChatFrame extends WindowAdapter { public void windowClosing(WindowEvent e) { pwr.println("@@##@@"); pwr.flush(); loop=false; } }
public void run() { String texto=null;
while(loop) { try { texto=brdr.readLine(); if (texto.equals("@@##@@")) { JOptionPane.showMessageDialog(ChatFrame.this,"Fin de Conexion"); loop=false; } else box.append("> "+texto+"\n"); } catch(IOException ex) { JOptionPane.showMessageDialog(ChatFrame.this,"IO Error"); } } pwr.println(""); pwr.flush(); setVisible(false); try { socket.close(); } catch(IOException e) { JOptionPane.showMessageDialog(ChatFrame.this,"Erro Pechando socket"); } if (!server) System.exit(0); }
ChatFrame(StreamSocket sk,boolean srv) { this(sk,"Server",srv); }
ChatFrame(StreamSocket sk,String n,boolean srv) { GridBagConstraints cs=new GridBagConstraints(); GridBagLayout gbl=new GridBagLayout();
user=n; socket=sk.getSocket(); brdr=sk.getSocketReader(); pwr=sk.getSocketWriter(); server=srv; setTitle("Chat Server: "+user); setLocation(190,90); setSize(600,500); getContentPane().setLayout(gbl);
cs.fill=GridBagConstraints.BOTH; cs.gridx=0; cs.gridy=0; cs.gridwidth=1; cs.gridheight=1; cs.weightx=100; cs.weighty=96; gbl.setConstraints(box,cs); box.setEditable(false); getContentPane().add(box);
cs.gridx=0; cs.gridy=1; cs.gridwidth=1; cs.gridheight=1; cs.weightx=100; cs.weighty=2; gbl.setConstraints(txt,cs); getContentPane().add(txt);
cs.gridx=0; cs.gridy=2; cs.gridwidth=1; cs.gridheight=1; cs.weightx=100; cs.weighty=2; gbl.setConstraints(btn,cs); getContentPane().add(btn); btn.addActionListener(new SendData()); this.addWindowListener(new CloseChatFrame()); setVisible(true); new Thread(this).start(); } }

Ficheiro StreamSocket.java (Clase auxiliar)
import javax.swing.*; import java.net.*; import java.io.*;
class StreamSocket { BufferedReader brdr; PrintWriter pwr; Socket socket;
StreamSocket(Socket sk) { socket=sk; try { brdr=new BufferedReader(new InputStreamReader(sk.getInputStream())); pwr=new PrintWriter(new BufferedOutputStream(sk.getOutputStream()),false); } catch (IOException e) { JOptionPane.showMessageDialog(null,"Erro Creando stream"); System.exit(0); } }
BufferedReader getSocketReader() { return brdr; }
PrintWriter getSocketWriter() { return pwr; }
Socket getSocket() { return socket; } }
Servlets
Tradicionalmente para tratar problemas como a actualización dunha base de datos a través da WEB é crear unha páxina HTML con campos de textos e o botón de 'submit'. Cando o usuario presiona o botón 'submit' os datos introducidos envíanse ó servidor xunto coa localización dun programa CGI (Common Gateway Interface) que executará o servidor WEB. Este programa deberá comprobar a corrección dos datos e compoñer unha páxina WEB de resposta ademáis de realizar a función que sexa. Os programas CGI escríbense normalmente en Perl, C, C++ ou calqueira linguaxe que poda acceder a entrada e saída estándar.
Os servlets pretenden sustituir ós programas CGI. Son programas JAVA que son executados polo servidor WEB. A arquitectura do API do servlet consta dun método service() que será o encargado de recibir as peticións dos clientes, e os métodos init() e destroy() que serán invocados ó cargar o servlet ou o descargalo.
public interface Servlet { public void init(ServletConfig config) throws ServletException; public ServletConfig getServletConfig(); public void service(ServletRequest req,ServletResponse res) throws ServletException, IOException; public String getServletInfo(); public void destroy(); }
O método getServletConfig() sirve para devolver un obxecto ServletConfig que contén os parámetros de inicialización e funcionamento do servlet e getServletInfo() devolve información sobre o servlet como o autor a versión ou o copyright.
A clase máis utilizada que implementa este interface é HttpServlet que está deseñada específicamente para o protocolo http, esta clase extende tamén ServletRequest e ServletResponse a HttpServletRequest e HttpServletResponse.
import javax.servlet.*; import javax.servlet.http.*; import java.io.*;
public class ServletExample extends HttpServlet { int i=0;
public void service(HttpServletRequest req, HttpServletResponse res) throws IOException { res.setContentType("text/html"); PrintWriter out=res.getWriter(); out.print("<HEAD><TITLE>"); out.print("Xerado por Servlet"); out.print("</TITLE></HEAD><BODY>"); out.print("

Resposta Servlet Num. "+i++); out.print("

</BODY>");

out.close();

}

}


JAVA Beans


Si se desenvolven partes dun programa de forma que sexan independentes, será posible ensamblar estes compoñentes en programas cun desenvolvemento máis eficiente. A maior dificultade do software de compoñentes é a gran variedade de microprocesadores e sistemas operativos, as arquitecturas de compoñentes VBX, OCX e ActiveX de Microsoft por exemplo tiveron unha gran implantación nos sistemas operativos Windows, pero non pasaron a ningún outro. JavaBeans pretende solucionar este problema.


JavaBeans permite o uso en entornos distribuídos, nos que os compoñentes se poden transmitir a través dunha conexión de Internet (a través de RMI, CORBA, DCOM ...) e está baseado na estructura de clases utilizadas para os applets, ademais outras características de JAVA como a introspección e a persistencia facilitan a creación dos mesmos.


A introspección consiste na capacidade dunha clase de informar sobre a súa propia constitución (os métodos que conten, por exemplo) e a persistencia refírese a capacidade dos obxectos JAVA de almacenar e recuperar posteriormente o seu estado.


JavaBeans proporciona o marco que fai posible a comunicación entre compoñentes e facilita a manipulación dos mesmos a través dun conxunto estándar de propiedades ben definidas a través dunha extensión da biblioteca de clases de JAVA chamada API de JavaBeans.

[#sdfootnote1anc 1] No caso particular da clase String, e por comodidade, é posible crear o obxecto sen o uso específico de new:


String nome = "Introducción".