Apuntes de Java Clases



Descargar 331,38 Kb.
Página1/3
Fecha de conversión23.03.2017
Tamaño331,38 Kb.
  1   2   3
Apuntes de Java

Clases
Las clases son como estructuras en C que admiten también sus propias funciones (métodos) y variables (como en C++). Las clases son por tanto una especie de plantillas. Los objetos son los ejemplos ( instances ) en que se materializan esas clases. La forma de declarar una clase es:

Modificador_de_la_clase class nombre_de_la_clase{


        Definición de la clase.......}

El modificador de clase puede contener tres palabras clave: public, final o abstract, syncronized.  El modificador public da una accesibilidad total a la clase, incluso desde otros ficheros y requiere que su nombre sea el del fichero en el que está. El modificador final tiene que ver con la herencia y no es muy usual; abstract y synchronized tienen  que ver respectivamente con los interfaces (y herencia ) y con los threads . Además detrás del nombre de la clase puede aparecer extends Clase_madre implements Interfaz. Estos últimos modificadores inidcan  la herencia o los interfaces .  Una vez declarada una clase, para crear un objeto que responda a la plantilla de esa clase, la forma más básica es:

Clase Objeto = new Clase();

Los paréntesis pueden contener, como se verá,  algunos parámetros si hay un método constructor definido en la clase.


En el siguiente ejemplo se define la clase Punto que está formada por dos números enteros y en main se crean dos objetos de tipo Punto: P y Q.

class Punto{
    int x;
    int y;
}



public class Proto{
    public static void main(String[] args){
        Punto P=new Punto();
        Punto Q=new Punto();
    }
}

La manera de acceder (si es posible) a un attributo (una "variable") de un objeto es  

Objeto.Atributo

La accesibilidad de los atributos de una clase  depende de los modificadores que pongamos al definirlos. Las posibilidadades son:



Modificador

Significado

private

Sólo puede accederse al atributo desde la clase

protected

Atributo accesible por las subclases y por todas las clases del mismo paquete

public

Accesible por todas las clases

por defecto

Accesible por todas las clases del mismo paquete

Además, tras estos modificadores pueden aparecer otros modificadores: static , final , transient y volatile .  Con static indicamos como en C que la variable es global. Sólo hay una parte de memoria reservada para el atributo correspondiente por muchos objetos que definamos correspondientes a esa clase. Los modificadores final y volatile tienen que ver respectivamente con la herencia y los threads .


Al ejecutar el siguiente ejemplo se obtiene (2,3).


class PuntoDosTres{
    int x=2;
    int y=3;
}

public class Proto{


    public static void main(String[] args){
        PuntoDosTres P=new PuntoDosTres();
        PuntoDosTres Q=new PuntoDosTres();
    System.out.println("("+P.x+","+Q.y+")");
    }
}

Al compilar se obtendría un error si int x=2 se sustituye por private int x=2 , concretamente:

Proto.java:10: x has private access in PuntoDosTres


        System.out.println("("+P.x+","+Q.y+")");

Pero todo funcionaría igual con protected int x=2 o public int x=2 ,

El operador   instanceof nos dice si un objeto es de cierta clase.

public class Proto{


    public static void main(String[] args){
        Object Car=new Object[1];

        System.out.println(Car instanceof String);


        System.out.println(Car instanceof Object);
    }
}

Da como resultado:

false
true


Aunque esto no parece ser de mucha utilidad.



Métodos
Las clases contienen normalmente métodos, esto es, funciones. La forma de acceder a un método de una clase es similar a la de los atributos. Basta escribir el nombre del objeto de la clase un punto y después el nombre del método:

Objeto.Método()

Los métodos tienen en líneas generales la sintaxis de las funciones de C pero  también admiten modificadores igual que los atributos. Las posibilidades de accesibilidad son como antes private , protected , public ; seguidos de  static , abstract, final , native , syncronized .  Lo único novedoso con respecto a los atributos o clases es native , que se utiliza para indicar que cierto método está en lenguaje nativo, típicamente C (es posible que parte de un programa java contenga código en C, aunque eso seguramente dé problemas al cambiar de plataforma, hay explicaciones y ejemplos en la ayuda de Sun ).  Además después de los argumentos de un método se puede añadir throws y la clase de una excepción, como se verá más adelante en el apartado de excepciones .

    El siguiente ejemplo completa el anterior, añadiendo métodos para asignar y recuperar las coordenadas de un objeto de la clase Punto.



public class Proto{
    public static void main(String[] args){
        Punto P=new Punto();
        P.ponx(3); P.pony(5);
   System.out.println("("+P.dimex()+","+P.dimey()+")");
    }
}

class Punto{


    int x,y;

    public void ponx(int primeracoord){


        x=primeracoord;
    }
    public void pony(int segundacoord){
        y=segundacoord;
    }

    public int dimex(){


        return x;
    }
    public int dimey(){
        return y;
    }
}





Para ver el efecto del modificador static , si cambiamos el método  main por:



public static void main(String[] args){
        Punto P=new Punto();
        Punto Q=new Punto();
        P.ponx(3); P.pony(5);
        Q.ponx(7); Q.pony(15);
        System.out.println("("+P.dimex()+","+P.dimey()+")");
        System.out.println("("+Q.dimex()+","+Q.dimey()+")");
}

el resultado será: (3,5), (7,15). Pero si cambiamos int x,y; por   static  int x,y; el resultado será (7,15), (7,15). Las variables del objeto Q interfieren con las de P, porque no hay memoria separada para ambos juegos de atributos.

Los atributos y métodos estáticos tienen la propiedad de que se puede acceder a ellos sin que se declare ningún objeto correspondiente a la clase. Por ejemplo,  tiene sentido el programa:



class Punto{
    static int x,y;
  }

public class Proto{


    public static void main(String[] args){
         Punto.x=13; Punto.y=34;
        System.out.println("("+Punto.x+","+Punto.y+")");
    }
}

Si embargo, si los atributos no fueran estáticos se obtendría un error del tipo:   non-static variable cannot be referenced from a static context.


Constructores

Al definir un objeto podemos querer  especificar el valor de algunos atributos. La manera de hacerlo es dotando a la clase correspondiente de un método constructor . Éste es un método que tiene el mismo nombre que la clase, que se suele colocar en primer lugar y que se ejecuta automáticamente.
Si un constructor tiene  ciertos parámetros como argumentos, se pueden especificar al declarar el objeto con

Clase Objeto = new Clase(parámetros);

Por ejemplo, con la siguiente variante de la clase Punto, se puede escribir Punto P= new Punto(31,22) para especificar que P es el punto (31,22).


class Punto{
    int x,y;

    Punto(int abcisa, int ordenada){


        x=abcisa;
    y=ordenada;
    }
}

public class Proto{
    public static void main(String[] args){
        Punto P=new Punto(31,22);
        System.out.println("("+P.x+","+P.y+")");
    }
}

En contraposición a los constructores, se puede declarar en cada clase un destructor ,  que es un método que se declara con

protected void finalize(){ /* Código del destructor */ }

y que se llama antes de que el recolector de basura (el gestor de memoria) elimine un objeto. Parece que no es demasiado común ni fácil de emplear (hay un ejemplo en los consejos de Sun ).



Sobrecarga

Tanto los constructores como los otros métodos de una clase, se pueden sobrecargar . Esto significa que se pueden definir simultáneamente de diferentes formas (con distintos tipos o número de argumentos). Por ejemplo, para manipular vectores en cartesianas o polares podemos crear la clase Vector con constructores de la forma siguiente:




class Vector{
    double x,y;
    // Primer constructor
    Vector(double abcisa, double ordenada){
        x=abcisa;
        y=ordenada;
    }

    // Segundo constructor


    Vector(double radio, double angulo, String tipo){
        if(tipo.equals("polares")){
            x=radio*Math.cos(angulo);
            y=radio*Math.sin(angulo);
    }
    }
}

public class Proto{
    public static void main(String[] args){
        Vector v=new Vector(31,22);
        System.out.println("("+v.x+","+v.y+")");
        Vector w=new Vector(1,(Math.PI)/4,"polares");
        System.out.println("("+w.x+","+w.y+")");
    }
}


El primer constructor requiere dos parámetros que son las coordenadas del punto, y el segundo constructor requiere tres parámetros: los dos primeros son las coordenadas en polares (radio y ángulo) y el tercero una cadena que cuando es "polares" procede a cambiar a coordenadas cartesianas. Así el resultado en pantalla es: (31.0,22.0) (0.707...,0.707...).  Escribir  por error   Vector w=new Vector(1,(Math.PI)/4,"plares"); en la declaración de w causa que el resultado sea (0,0) para este vector. Si queremos que con cualquier cadena que no sea "polares" se interpreten las coordenadas como rectangulares, se puede escribir al comienzo del segundo constructor this(radio,angulo) . La palabra reservada this significa en este contexto la clase en la que se está, es decir, Vector . Parece que this sólo se puede emplear al comienzo de un constructor. El resultado será entonces (1.0,0.7853981633974483)
.


Herencia

Una clase puede heredar de otra atributos o métodos. Se dice que la primera es subclase de la segunda, o que la segunda es la superclase. A diferencia de lo que ocurre en C++ no hay herencia múltiple (aunque se puede simular con los interfaces ), es decir, cada clase tiene a lo más una superclase. Para definir una subclase se escribe:

class Nombre_de_la_subclase extends Nombre_de_la_superclase

Evidentemente sólo se heredan los atributos y métodos cuyos modificadores lo permitan (no los private ).

Ejemplo:


class Segmento{
    public double x1,y1,x2,y2;

    double longitud(){


        return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    }
}

class SegmentoOrientado extends Segmento{
    public int orient;

    public void ImprimeOrientacion(){


        switch(orient){
            case 1:
                System.out.println("Está orientado de ("
                +x1+","+y1+") a ("+x2+","+y2+")");
                return;
            case -1:
                System.out.println("Está orientado de ("
                +x2+","+y2+") a ("+x1+","+y1+")");
                return;
            default:
                System.out.println(
                "Todavía no tiene una orientación válida");
                return;
        }
    }
}

Al ejecutar




public class Proto{
    public static void main(String[] args){
    SegmentoOrientado s=new SegmentoOrientado();
    s.x1=1.0; s.y1=5.0; s.x2=4.0; s.y2=9.0;
    s.orient=-1;
    System.out.println("El segmento conecta ("
        +s.x1+","+s.y1+") con ("+s.x2+","+s.y2+")");
    System.out.println("Su longitud es s "+s.longitud());
    s.ImprimeOrientacion();
    }

}

se obtiene, como cabría esperar, "El segmento conecta (1.0,5.0) con (4.0,9.0)    Su longitud es s 5.0     Está orientado de (4.0,9.0) a (1.0,5.0)". Lo que muestra que efectivamente los métodos y atributos de Segmento se han añadido a los de la subclase SegmentoOrientado sin necesidad de definirlos de nuevo.

Una subclase puede tener su propio constructor que incluso puede sobrecarga al de su superclase (incluso con el mismo tipo y número de parámetros). Normalmente un constructor de una subclase empleará el de la superclase, para invocarlo se emplea la sentencia super() . Debe aparecer necesariamente como primera instrucción en el constructor. Por ejemplo, incluyendo en Segmento y en SegmentoOrientado los constructores:




   Segmento(double a,double b,double c,double d){
        x1=a; y1=b; x2=c; y2=d;
    }

SegmentoOrientado(double a,double b,double c,double d, int e){
        super(a,b,c,d);
        orient=e;
    }

entonces se podría haber hecho en main la declaración.




  SegmentoOrientado s=new SegmentoOrientado(11.0,2.0,3.0,7.0,-1);

Es importante tener en cuenta que si la sentencia super no aparece explícitamente en el constructor de una subclase o si no hay constructor, entonces el compilador  inserta super() . Por ejemplo, no serían válidas la siguiente definición y el siguiente constructor de Segmento si no hay constructor en SegmentoOrientado , porque al ejecutarse super() en la clase  SegmentoOrientado se llamaría a un constructor sin parámetros que no existe.


    Como ya se ha mencionado, se puede redefinir (sobrecargar) un constructor de una clase en algunas de sus subclases, incluso permitiendo el mismo tipo y número de parámetros. Así sería un constructor válido  en SegmentoOrientado :


    SegmentoOrientado(double a,double b,double c,double d){
        super(a,b,c,d);
        x1=c; y1=d; x2=a; y2=b;
    }

y también suprimiendo super(a,b,c,d) si se añade a  Segmento el constructor   vacío Segmento(){} .


Este tipo de sobrecargaz no es particular de los constructores sino que se extiende al resto de los métodos. Por ejemplo, se podría incluir en SegmentoOrientado una de las siguientes declaraciones:


   double longitud(){
          return orient*Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    }

double longitud(){
        return orient*super.longitud();
    }

En la segunda se emplea super de una manera distinta que en los constructores y no necesariamente como primera sentencia, pero de nuevo se refiere al método homónimo en la superclase.

    Si un método se va a redefinir en todas las subclases entonces no merece la pena que tenga cuerpo (instrucciones).  En ese caso se le declara como abstracto por medio del modificador abstract . Esto es como los prototipos de funciones en C, una señal que indica que se va a definir una función de cierto tipo pero sin darla explícitamente. Si una clase tiene uno o más métodos abstractos, debe también llevar el modificador abstract . Por ejemplo, antes, si no se usase super.longitud() ,  se podría haber sustituido la definición del método longitud() en Segmento por


abstract double longitud();

en cuyo caso  la clase Segmento se debería declara como abstract class Segmento .

El modificador final aplicado  a clases, métodos o atributos, impide en cierto modo la herencia.  Concretamente,


 final class Nombre_de_clase{....}

prohíbe que se puedan crear subclases de Nombre_de_clase , mientras que




final Metodo(...){...};


impide que en alguna subclase se pueda redefinir (sobrecargar) este método.  En un atributo, el modificador final  impide que se altere su valor en una subclase, por ejemplo, class Madre{ final int n;}   class Hija extends Madre{ int m=++n;} causaría un error.

En java todavía no existe el tipo de datos constante , pero se puede emular empleando los modificadores static final . Con static se asegura que no habrá copias diferentes suyas en diferentes objetos (sólo una copia en la memoria), y con final se impide que se pueda modificar en ninguna subclase.



Polimorfismo

El polimorfismo es la propiedad que tienen los objetos de poder ser considerados de diferentes clases realcionadas por herencia. Hay a grandes rasgos una analogía con los tipos de datos. Los enteros y los reales son distintos pero muchas veces se permite que un número entero actúe como argumento en lugar de uno real y siempre se puede usar una conversión de tipo, como (float) n, si fuera necesaria. El polimorfismo permite que si un objeto se crea a través de un constructor de una subclase pueda usar preferentemente los métodos sobrecargados de dicha subclase pero no el resto, si no hay conversión de tipos, porque desde una clase no se puede acceder a los métodos no sobrecargados de las subclases. Por ejemplo:




class Elipse{
    double ejeX, ejeY;

    Elipse(double a,double b){


        ejeX=a; ejeY=b;
    }

    double Area(){


        return Math.PI*ejeX*ejeY;
    }

    String prueba(){


        return "Método de class Elipse";
    }

}


class  Circulo extends Elipse{

    Circulo(double r){


        super(r,r);
    }

    double Longitud(){


        return 2*Math.PI*ejeX;
    }

    String prueba(){


        return "Método de class Circulo";
    }
}

 

Con el método main



public class Proto{
    public static void main(String[] args){
    Elipse obj1=new Circulo(10);
    Circulo obj2=new Circulo(10);
    Elipse obj3=new Elipse(10,1);

//INSTRUCCIONES 


    }

}

Al sustituir  //INSTRUCCIONES f como se indica, se obtienen los siguientes resultados:


Instrucciones

Resultado y Explicación

System.out.println(obj1.Longitud());

Error al compilar. Desde Elipse no se ve Longitud().

System.out.println(obj1.prueba());

Método de class Circulo. Al estar sobrecargado, busca el método de la subclase.

System.out.println(obj1.Area());

100*PI

System.out.println(obj2.Longitud());

20*PI

System.out.println(obj2.prueba());

Método de class Circulo

System.out.println(obj2.Area());

100*PI La subclase hereda los métodos de la superclase.

System.out.println(obj3.Area());

10*PI

System.out.println(((Circulo)obj1).Longitud());

20*PI Para aplicar el método Longitud a Elipse hay que transformarlo en Circulo.


Interfaces

Los interfaces son clases en las que todos los atributos son public static final (constantes públicas) y los métodos son abstract . No es necesario indicar explícitamente estos modificadores. Los interfaces se comportan en cierta forma como los ficheros de cabecera en C, especificando constantes y prototipos de funciones.


Un interfaz se define igualque una clase sustituyendo class por interface. Para que una clase pueda usar un interface hay que añadir detrás de su nombre

implements Nombre_del_interface

Se admite el  uso de varios interfaces separando sus nombres por comas. Los interfaces también admiten herencia, de hecho herencia múltiple. Por ejemplo, es válido:

public interface Nombre1 extends Nombre2, Nombre3{...}

aunque no parece que haya gran diferencia con utilizar implements con varios interfaces.

Un ejemplo que involucra herencia de clases e interfaces es el siguiente:




interface PoligReg{
    double raiz3=Math.sqrt(3);
    double tg36=Math.tan(Math.toRadians(36.0));

    int Nlados();


    double Area();
}

class PolReg{


    double lado;
    String nombre;
    PolReg(double l, String c){
        lado=l; nombre=c;
    }
}

class Triangulo extends PolReg implements PoligReg{
    Triangulo(double l){super(l,"Triángulo");}
    public int Nlados(){return 3;}
    public double Area(){return lado*raiz3/2;}
}

class Cuadrado extends PolReg implements PoligReg{


    Cuadrado(double l){super(l,"Cuadrado");}
    public int Nlados(){return 4;}
    public double Area(){return lado*lado;}
}

class Pentagono extends PolReg implements PoligReg{


    Pentagono(double l){super(l,"Pentágono");}
    public int Nlados(){return 5;}
    public double Area(){return lado*lado*1.25/tg36;}
}

En el interfaz PoligReg se definen algunas constantes y se da el prototipo de los métodos Nlados y Area . La definición de estos métodos se hace en cada clase que emplea el interfaz. Al ejecutarel siguiente código se obtiene el resultado de la derecha:




public class Proto{
    public static void main(String[] args){
    Triangulo tr=new Triangulo(20);
    System.out.println(tr.nombre+", "+"N.lados="+tr.Nlados()
            +", "+ " Área="+tr.Area());
    Cuadrado cu=new Cuadrado(10);
    System.out.println(cu.nombre+", "+"N.lados="+cu.Nlados()
            +", "+ " Área="+cu.Area());
    Pentagono pe=new Pentagono(1);
    System.out.println(pe.nombre+", "+"N.lados="+pe.Nlados()
            +", "+ " Área="+pe.Area());
    }
}

Triángulo, N.lados=3,  Área=17.32050807568877
Cuadrado, N.lados=4,  Área=100.0
Pentágono, N.lados=5,  Área=1.7204774005889671


Otra forma de proceder habría sido añadir los métodos abstractos Nlados y Area a la clase PolReg y después sobrecargarlos en las subclases. Una diferencia al emplear un interfaz es que el compilador  muestra un mensaje de error cuando no se sobrecargan estos métodos en todas las clases (habría declararlas como abstractyas si tienen métodos sin definir). Al combinar la herencia de clases con los interfaces se tiene una simulación de la herencia múltiple, esto es, de la posibilidad de que una clase tenga varias superclases.
  1   2   3


La base de datos está protegida por derechos de autor ©absta.info 2016
enviar mensaje

    Página principal