martes, 6 de agosto de 2013

MÉTODOS ESTÁTICOS EN JAVA


INTRODUCCION


Los métodos estáticos se caracterizan porque son métodos que se consideran propios de la clase y por lo tanto no pueden ser referenciados por un objeto específico de la clase. Esta característica hace que para referenciar un método estático se utiliza el nombre de la clase. Por ejemplo, si el método sumar() es un método estático de la clase Operaciones; entonces el método sumar() se referenciaría en una aplicación como: Operaciones.sumar(). Cuando la clase donde se encuentra definido el método estático es la clase ejecutable de la aplicación no es necesario anteponerle el nombre de la clase cuando se llame desde el método estático main() de la clase ejecutable.

Generalmente, los métodos estáticos se denominan métodos de la clase y no pueden manejar datos que definen valores de los atributos de los objetos; es decir sólo pueden manejar datos de valor o datos definidos dentro del mismo método.

MODELADO DE LOS MÉTODOS ESTÁTICOS


En un diagrama de clases UML que modela una clase, los métodos estáticos se denotan con un subrayado de la definición de la signatura del método.


Figura 1. Diagrama de clases de ManejadoraFechas
 


 
En el diagrama de la figura 1 se tiene tres métodos estáticos los cuales son definidos para la clase ejecutable llamada ManejadoraFechas. Una clase se denomina la clase ejecutable si contiene el método estático main(). Una aplicación sólo puede tener una clase ejecutable. El objetivo del método main() es definir la entrada del sistema a la ejecución de la aplicación y esta es la razón fundamental para el método main() sea estática, porque de lo contrario tendría antes de llamar al método main() crear un objeto de la clase ejecutable, lo cual sería imposible antes de entrar el sistema a la aplicación.

PROGRAMACIÓN DE LOS MÉTODOS ESTÁTICOS


En Java los métodos estáticos se identifican por el modificador static. El formato general de la definición de un método estático es como se muestra a continuación:
 

    <visibilidad> static <tipo> nombre_método(argumentos){ … }

 
Vea en la programación de la clase java llamada ManejadoraFechas que se muestra en el código de la siguiente aplicación.
 

Aplicación 1 Java
package aplicacion;
import java.io.*;
import java.util.*;
public class ManejadoraFechas {
    public static void main(String[] args) {       
        int año;
        String mensajeError="Fuera del calendario Gregoriano\n o del año actual";
        try{
         BufferedReader leerEntrada=new BufferedReader(new InputStreamReader(System.in));
         System.out.print("Digite año: ");
         año=Integer.parseInt(leerEntrada.readLine());
         if(!esValido(año)) throw new Exception(mensajeError);
         if(esBisiesto(año))System.out.println(año+" es bisiesto");
         else System.out.println(año+" no es bisiesto");        
        }catch (Exception e){
           System.err.println(e.getMessage());
        }
    }
 public static boolean esBisiesto(int año){
        return (año%4==0)||((año%100!=0)&&(año%400==0));
    }
 public static boolean esValido(int año){
      Calendar fechaHoy=Calendar.getInstance();
      return año>=1582 && año<=fechaHoy.get(Calendar.YEAR);     
    }
}
 

La clase ManejadoraFechas es la clase ejecutable de la aplicación y contiene tres métodos estáticos, denominados:

1) El método main(). Es un método estático y que tiene como objetivos definir la entrada del sistema a la aplicación para ejecutar los programas de la misma y un segundo objetivo es definir el monitoreo o control de las ejecución de los programas que componen la aplicación.

2) El método esPrimo(). Es un método estático y tiene como objetivo averiguar si el valor entero que recibe como argumento corresponde a un año bisiesto. Un año es bisiesto si divisible por cuatro excepto que sean divisibles por cien a menos que sea divisible por cuatrocientos.

3) El método esValido(). Es un método estático y tiene como objetivo averiguar si el valor entero entregado como argumento corresponde a un año del calendario gregoriano hasta el año actual. El calendario gregoriano se introdujo en el año 1582 por el papa Gregorio XIII antes de este regía el calendario juliano.

REGLAS


ü  Regla 1. Los métodos estáticos no pueden procesar valores de atributos de objetos.

ü  Regla 2. Los métodos estáticos se utilizan cuando los valores son comunes para todos los objetos de la clase.

ü  Regla 3. En una aplicación sólo puede existir un método estático main

ü  Regla 4. Para llamar a su ejecución un método estático se hace mediante el nombre de la clase, cuando este método estático se define dentro de la clase ejecutable no se requiere el nombre de la clase cuando se llama dentro del método main.

 

CASO DE ESTUDIO


Modelar en UML y programar en java una aplicación para permitir a sus usuarios calcular el IMC (índice de masa corporal). El IMC es un indicador medico que relaciona entre el peso (kilogramos) y la estatura (metros) de una persona ideado por el estadístico belga L. A. J. Quetelet. Este indicador se calcula según la siguiente fórmula matemática:

 
IMC = peso/(estatura*estatura)

Este modelo matemático es válido para hombres y mujeres entre edades de 20 y 60 años. Es decir no aplica para personas fuera de este rango de edades.

 

Tabla 1
Rangos IMC
Diagnostico
Por debajo de 18.5
Por debajo del peso
Entre  18.5 y 24.9
Saludable
Entre 25.0 y 29.9
Con sobrepeso
Entre 30.0 y 39.9
Obeso
Superior a 39.9
Obesidad extrema o de alto riesgo

 

La aplicación debe permitirle a una persona (hombre o mujer) entre 20 y 60 años consultar su IMC en un cuadro de diálogos que le solicita: la edad, peso (en kilogramos) y estatura (en centímetro).

ESPECIFICACIÓN DE REQUERIMIENTOS


 
Figura 2. Diagrama de casos de uso

 

El diagrama de casos de uso especifica los requerimientos del usuario al sistema. El usuario espera que el sistema le calcule el índice de masa corporal y le haga un diagnostico de acuerdo a este valor del IMC.

DISEÑO CUADRO DE DIÁLOGO


La interacción del usuario con el sistema software de la aplicación lo debe hacer con un cuadro de diálogos como el que se muestra en la figura 3. Este cuadro de diálogos es de tipo JFrame el cual contiene: cinco objetos de tipo JTextField, siete objetos de tipo JLabel y un objeto de tipo JButton. El usuario digita en cada cuadro de texto el dato correspondiente y para que el sistema lo acepte pulsa la tecla ENTER. Es importante que el sistema valide los datos cada vez que el usuario entra un carácter a los cuadros de texto. En el caso que pulse una tecla que no corresponda a un carácter valido del formato del dato el sistema borra lo digitado y sigue pidiendo los valores del dato. El dato para la edad debe ser un entero positivo sin signo. Los datos para el peso, la estatura deben ser numérico decimal (con cifras decimales o no) positivo sin signo. El sistema debe validar el dato de la edad y en el caso que la edad no esté entre 20 y 60 años el sistema debe informarle mediante un cuadro de mensajes y no permitirle continuar introducir los datos del peso y la estatura. El sistema se restaurara para que el usuario introduzca otro dato de edad.

 

Figura 3. Cuadro de diálogo de aplicación para el cálculo del IMC

 

Desde el punto de vista de la programación orientada a objetos, el cuadro de diálogos de la figura 3 corresponde a la representación visual del objeto de una subclase de la clase JFrame.

DISEÑO ARQUITECTÓNICO DE LA APLICACIÓN


El diseño arquitectónico proporciona la vista para el montaje del esqueleto del sistema. En la figura 4 se muestra el esqueleto del sistema para calcular índice de masa corporal de una persona hombre o mujer entre 20 y 60 años. El programador se da cuenta al analizar esta vista que debe crear un nuevo proyecto llamado calculoimc al cual se le debe agregar tres clases java llamadas CalculoIMC, VentanaPrincipal y IndiceMasaCorporal.


Figura 4. Vista arquitectónica



La clase CalculoIMC es la clase ejecutable la cual tiene una dependencia de la clase VentanaPrincipal. La clase VentanaPrincipal es una subclase de la clase JFrame del paquete javax.swing, es decir que entre la subclase VentanaPrincipal y la clase JFrame hay una relación de herencia y adicionalmente la subclase VentanaPrincipal y la clase IndiceMasaCorporal hay una relación de dependencia.

Al existir una relación de herencia entre la subclase VentanaPrincipal y la clase JFrame es posible definir una serie de objetos que constituyen al cuadro de diálogos de la figura 3 esto debido que la subclase hereda todos los componentes definidos en la clase JFrame.

 PROGRAMACIÓN DE LA CLASE CalculoIMC

 
Figura 5. Diagrama de clases de la clase CalculoIMC


 

package calculoimc;
public class CalculoIMC {
    public static void main(String[] args) {
        VentanaPrincipal diag=new VentanaPrincipal();       
    }
}

PROGRAMACIÓN DE LA CLASE VentanaPrincipal


 
Figura 6. Diagrama de clases de la clase VentanaPrincipal


 

package calculoimc;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class VentanaPrincipal extends JFrame implements ActionListener,KeyListener{
    JButton btnCancelar;
    JTextField txtEdad,txtPeso,txtEstatura,txtIMC,txtDiagnostico;
    JLabel labEdad,labEdad1,labPeso,labPesoUnidades;
    JLabel labEstatura,labEstaturaUnidades,labIMC;
    JPanel panel1,panel2;
    Color colorFondo,colorTxt,colorFuente;
    Font fuente;
    static int edad;
    public static int estado;
    static float peso,estatura;
    public VentanaPrincipal(){
        this.setSize(410,280);
        this.setLocationRelativeTo(null);
        this.setTitle("Calculo IMC (Válido para personas entre 20 y 60 años)");
        this.setResizable(false);
        this.setLayout(null);
        colorFondo=new Color(110,155,155);
        colorTxt=new Color(150,170,170);
        colorFuente=new Color(255,255,0);
        fuente= new Font("serief",Font.BOLD,12);
        this.getContentPane().setBackground(colorFondo);
        inicializarComponentes();
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }   
    private void inicializarComponentes(){
        panel1=new JPanel();
        panel1.setBounds(5,10,395,40);
        panel1.setBorder(BorderFactory.createEtchedBorder());
        panel1.setLayout(null);       
        labEdad=new JLabel("Digite su Edad:");
        labEdad.setBounds(10,10,100,20);
        labEdad.setFont(fuente);
        labEdad.setForeground(colorFuente);
        panel1.add(labEdad);
        txtEdad=new JTextField();
        txtEdad.setBounds(110,10,40,20);
        txtEdad.setHorizontalAlignment(JTextField.RIGHT);
        txtEdad.setBorder(BorderFactory.createBevelBorder(1));
        txtEdad.setBackground(colorTxt);
        txtEdad.setForeground(colorFuente);
        panel1.add(txtEdad);       
        labEdad1=new JLabel("(años cumplidos)");
        labEdad1.setBounds(160,10,120,20);       
        panel1.add(labEdad1);
        panel1.setBackground(colorFondo);
        labEdad1.setForeground(colorFuente);
        this.add(panel1);      
        txtEdad.addKeyListener(this);
        panel2=new JPanel();
        panel2.setBounds(5,60,395,185);
        panel2.setBorder(BorderFactory.createEtchedBorder());
        panel2.setLayout(null);
        labPeso=new JLabel("Peso: ");
        labPeso.setBounds(50,10,60,20);
        panel2.add(this.labPeso);
        labPeso.setForeground(colorFuente);
        txtPeso=new JTextField();
        txtPeso.setBounds(120,10,60,20);
        txtPeso.setHorizontalAlignment(JTextField.RIGHT);
        txtPeso.setBorder(BorderFactory.createBevelBorder(1));
        txtPeso.setBackground(colorTxt);
        txtPeso.setEditable(false);
        txtPeso.setForeground(colorFuente);
        panel2.add(this.txtPeso);
        labPesoUnidades=new JLabel("Kilogramos");
        labPesoUnidades.setBounds(190,10,80,20);
        labPesoUnidades.setForeground(colorFuente);
        panel2.add(this.labPesoUnidades);       
        labEstatura=new JLabel("Estatura:");
        labEstatura.setBounds(50,50,60,20);
        labEstatura.setForeground(colorFuente);
        panel2.add(this.labEstatura);
        txtEstatura=new JTextField();
        txtEstatura.setBounds(120,50,60,20);
        txtEstatura.setHorizontalAlignment(JTextField.RIGHT);
        txtEstatura.setBorder(BorderFactory.createBevelBorder(1));
        txtEstatura.setBackground(colorTxt);
        txtEstatura.setForeground(colorFuente);
        txtEstatura.setEditable(false);
        panel2.add(txtEstatura);
        labEstaturaUnidades=new JLabel("Centimetros");
        labEstaturaUnidades.setBounds(190,50,80,20);
        labEstaturaUnidades.setForeground(colorFuente);
        panel2.add(this.labEstaturaUnidades);
        labIMC=new JLabel("IMC:");
        labIMC.setBounds(50,90,60,20);
        labIMC.setForeground(colorFuente);
        panel2.add(this.labIMC);       
        txtIMC=new JTextField();
        txtIMC.setBounds(120,90,60,20);
        txtIMC.setHorizontalAlignment(JTextField.RIGHT);
        txtIMC.setBorder(BorderFactory.createBevelBorder(1));
        txtIMC.setBackground(colorTxt);
        txtIMC.setForeground(colorFuente);
        txtIMC.setEditable(false);
        panel2.add(txtIMC);       
        txtDiagnostico=new JTextField();
        txtDiagnostico.setBounds(50,130,300,40);
        txtDiagnostico.setHorizontalAlignment(JTextField.CENTER);
        txtDiagnostico.setBorder(BorderFactory.createBevelBorder(1));
        txtDiagnostico.setBackground(colorTxt);
        txtDiagnostico.setEditable(false);
        panel2.add(txtDiagnostico);
        btnCancelar=new JButton("Cancelar");
        btnCancelar.setBounds(280,40,100,40);
        btnCancelar.setBorder(BorderFactory.createBevelBorder(0));
        btnCancelar.setBackground(colorTxt);
        btnCancelar.setForeground(colorFuente);
        panel2.add(this.btnCancelar);
        panel2.setBackground(colorFondo);
        this.add(panel2);       
        txtPeso.addKeyListener(this);
        txtEstatura.addKeyListener(this);
        btnCancelar.addActionListener(this);
    }   
    // Programación modelo dinámico   
    public void actionPerformed(ActionEvent ev){       
        if(ev.getSource()==btnCancelar) cancelar();        
    }   
    public void keyPressed(KeyEvent ev){      
       if(ev.getSource()==txtPeso){
           if((ev.getKeyChar()=='\n')&&(!txtPeso.getText().isEmpty())){                          
               txtPeso.setEditable(false);
               txtEstatura.setEditable(true);              
               txtEstatura.requestFocus();
           }                           
       }else if(ev.getSource()==txtEstatura){
           if((ev.getKeyChar()=='\n')&&(!txtEstatura.getText().isEmpty())){              
             txtEstatura.setEditable(false);       
             txtIMC.setText(IndiceMasaCorporal.toStringIMC(peso, estatura));            
             txtDiagnostico.setText(IndiceMasaCorporal.obtenerDiagnostico(peso,estatura));
             if(estado==1)txtDiagnostico.setForeground(new Color(255,0,0));
             else txtDiagnostico.setForeground(colorFuente);           
           }                 
       }
    }
    public void keyReleased(KeyEvent ev){
       if(ev.getSource()==txtEdad){
           try{
              edad=Integer.parseInt(txtEdad.getText());             
           }catch (Exception e){
               txtEdad.setText("");
           }
          
       } else if(ev.getSource()==txtPeso){
           try{
              peso=Float.parseFloat(txtPeso.getText());             
           }catch (Exception e){
               txtPeso.setText("");
           }          
       }else if(ev.getSource()==txtEstatura){
           try{
              estatura=(Float.parseFloat(txtEstatura.getText()))/100F;             
           }catch (Exception e){
               txtEstatura.setText("");
           }          
       } 
    }
    public void keyTyped(KeyEvent ev){
        String mensaje1="Fuera del rango de edad\n(consulte el médico)";
        if(ev.getSource()==txtEdad){
            if(ev.getKeyChar()=='\n' && !txtEdad.getText().isEmpty()){
                if(edad<20 || edad>60){
                  JOptionPane.showMessageDialog(this,mensaje1);                  
                  cancelar();
                }else{
                  txtPeso.setEditable(true);               
                  txtPeso.requestFocus(); 
                }               
            }
        }
    }
     private void cancelar(){
        txtEdad.setText("");
        txtEdad.setEditable(true);
        txtPeso.setText(null);
        txtPeso.setEditable(false);
        txtEstatura.setText(null);
        txtEstatura.setEditable(false);
        txtIMC.setText(null);       
        txtDiagnostico.setText(null);
        txtEdad.requestFocus();
    }   
}

PROGRAMACIÓN DE LA CLASE IndiceMasaCorporal


 

Figura 7. Diagrama de clases de la clase IndiceMasaCorporal

 

package calculoimc;
import java.text.*;
public class IndiceMasaCorporal {
    static String strDiagnosticos[]={"Por debajo del peso","Saludable","Con sobrepeso",
    "Obeso","Obesidad extrema o de alto riesgo"};
    public static String toStringIMC(float peso,float estatura){
        DecimalFormat dec=new DecimalFormat("###.##");
        return (dec.format(peso/(estatura*estatura)));
    }
    public static String obtenerDiagnostico(float peso, float estatura){
        float imc=peso/(estatura*estatura);
        int indice=4;
        VentanaPrincipal.estado=1;
        if(imc<18.5)indice=0;
        else if(imc<=24.9){indice=1;VentanaPrincipal.estado=0;}
        else if(imc<=29.9)indice=2;
        else if(imc<=39.9)indice=3;
        return strDiagnosticos[indice];
    }
}

LA DIFERENCIA ENTRE MÉTODOS ESTÁTICOS Y NO ESTÁTICOS


Algunas de las diferencias entre métodos estáticos y no estáticos son:

·         Los métodos estáticos solo pueden acceder a miembros estáticos (datos y métodos) y los métodos no estáticos pueden acceder tanto miembros estáticos como no estáticos.

·         Los métodos no estáticos solo pueden ser llamados por los objetos de la clase a la pertenecen y los métodos estáticos pueden ser llamados tanto por la clase como por los objetos.

·         Los métodos estáticos se implementan cuando en un futuro no se le implementan miembros no estáticos. Por ejemplo, cuando se quiere aplicar el modelo procedimental en lenguajes orientados a objetos. Es una manera elegante de no utilizar las ventajas de la programación orientada a objetos.

Aunque con algunas excepciones, los métodos estáticos son pocos recomendables en sistemas complejos. Yo diría que en curso de programación procedimental los métodos estáticos son la solución para comprender el concepto de la programación procedimental. Esta sería la solución para aprender la programación procedimental con un lenguaje orientado a objetos puro, como el java.

DATOS ESTÁTICOS


De la misma manera como existen métodos estáticos y no estáticos también existen los datos estáticos y no estáticos. Los datos estáticos o datos de valor tienen como característica fundamental que comparten el mismo espacio de memoria para todos los objetos creados y a diferencia de los datos no estático o de instancia cada objeto tiene su propio espacio de memoria para cada objeto creado. El formato para definir un dato estático es como sigue:

<visibilidad> static <tipo> nombre-variable {= valor inicial};

 
 Ejemplo. Aplicación ejemplo uso datos estáticos.

Figura 8. Diagrama de clases
 

Código fuente java de la clase EjemploEstaticos
package ejemploestaticos;
public class EjemploEstaticos {
    public static void main(String[] args) {
        Punto p,q,arrPuntos[]=new Punto[3];
        p=new Punto();
        q=new Punto(4.5F,5.0F);
        for(int i=0;i<arrPuntos.length;i++){
           arrPuntos[i]=new Punto();
        }
        System.out.println("Numero de puntos creado "+Punto.getNroPuntos());
        System.out.println("p= ("+p.getX()+" , "+p.getY()+")");
        System.out.println("q= ("+q.getX()+" , "+q.getY()+")");
        for(int i=0;i<arrPuntos.length;i++){
            System.out.println("arrPuntos["+i+"]= ("+arrPuntos[i].getX()+" , "+arrPuntos[i].getY()+")");
        }       
    }
}

 
Código fuente java de la clase Punto
package ejemploestaticos;
public class Punto {
    private float x,y;
    private static int nroPuntos=0;
    public Punto(float x, float y) {
        this.x = x;
        this.y = y;
        nroPuntos++;
    }
    public Punto() {
        this.x=this.y=0;
        nroPuntos++;
    }
    public float getX() {
        return x;
    }
    public void setX(float x) {
        this.x = x;
    }
    public float getY() {
        return y;
    }
    public void setY(float y) {
        this.y = y;
    }
    public static int getNroPuntos() {
        return nroPuntos;
    }
  
}

 La siguiente figura ilustra la diferencia de la forma como se almacenan en la memoria del computador los datos estáticos (datos de valor) y los datos no estáticos (datos de instancia).

Figura 9. Almacenamientos de datos estáticos y no estáticos
 
 
Observen que el dato estático nroPuntos ocupa un espacio de memoria al cual todos los objetos al mismo espacio de memoria. Una modificación del valor de esta variable estática por cualquier objeto es detectado por el resto de objetos.

 

 

 

 

No hay comentarios:

Publicar un comentario