APÉNDICE A
ELEMENTOS DE JAVA
Este apéndice contiene una introducción al lenguaje Java necesario para seguir los ejemplos de este libro. Esta introducción a Java es específica para Android. No trataremos los programas Java independientes ni los applets. Por tanto, todos los ejemplos podrán ejecutarse como aplicaciones Android en un emulador o en un teléfono o tablet. Esta aproximación a Java es algo inusual. Existe una idea establecida de que el lenguaje Java debe aprenderse antes que Android y de hecho todos los libros de Android suponen que el lector conoce Java u otro lenguaje similar como C++. Aunque la lógica de primero se empieza a andar y luego a correr es correcta, lo que es desacertado en en el presente caso es asociar a Android con correr. Aunque las aplicaciones profesionales de Android son programas de gran complejidad, esto no impide que se puedan escribir programas sencillos que puedan comprenderse y que permitan aprender el lenguaje. Al aprender Android y Java al mismo tiempo, se evita tener que asimilar conceptos de Java que Android no soporta, por ejemplo, los gráficos y los applets.
A.1. Programa básico de Java con Android
Utilizaremos el programa Eclipse para crear un nuevo proyecto de Android llamado EjemploJava1. Un proyecto de Android contiene un programa de Java y otros datos en una serie de ficheros, que son utilizados por el programa o por el sistema. Un programa de Java es una colección de clases, contenidas en uno o varios ficheros file1.java, file2.java,…. En Android estos ficheros java se almacenan en el directorio src de nuestro proyecto actual. Al crear nuestro nuevo proyecto de Android, se crea la actividad EjemploJava1, almacenada en el fichero EjemploJava1.java, que se encuentra en el directorio src. Al hacer doble click sobre este fichero en el explorador de archivos, se abre en el editor de Eclipse y  nos encontramos con el programa Java más simple:
  package es.ugr.amaro;
  import android.app.Activity;
  import android.os.Bundle;
  public class EjemploJava extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
 }
Este es el modelo que utilizaremos como punto de partida en todos nuestros programas Java. Examinemos su estructura:
   El programa contiene una serie de instrucciones o sentencias. En Java cada instrucción o sentencia debe terminar con un punto y coma. Se pueden escribir varias instrucciones en una línea o una instrucción en varia líneas. Los espacios en blanco son ignorados.
   La primera línea package es.ugr.amaro.ejemplojava1; indica que esta clase pertenece al paquete es.ugr.amaro. Cada punto en el nombre de un paquete indica un subdirectorio, por lo que nuestro programa Java está contenido en el directorio:
          src/es/ugr/amaro/ejemplojava1
   Las dos líneas siguientes precedidas por import indican que este programa utiliza dos clases predefinidas en dos paquetes del sistema Android.
          import android.app.Activity;
          import android.os.Bundle;
En este caso, el programa importa la clase Activity del paquete android.app y la clase Bundle del paquete android.os. Android contiene numerosos paquetes con miles de clases. En el editor de Eclipse, cuando escribimos el nombre de una clase, ésta se subraya en rojo si se requiere añadir un import, por lo que no necesitamos recordar el nombre del paquete. Basta pulsar con el botón derecho sobre el nombre de la clase y aparece una lista de opciones, entre ellas la de importar el paquete. La seleccionamos y Eclipse escribirá la instrucción import por nosotros.
   El programa contiene una clase llamada EjemploJava1 cuya definición abarca el bloque de código comprendido entre la llave inicial y final:
        public class JavaEjemplo1 extends Activity {
        }
Notese que la definición de la clase no termina con punto y coma. Esto es debido a que no se trata de una sentencia o instrucción ejecutable, sino un tipo de  definición. El atributo public indica que la clase es pública y puede ser utilizada externamente. La declaración de la clase finaliza con extends Activity. Esto significa que nuestra clase es una subclase de la clase Activity, definida en el paquete android.app y hereda todas sus propiedades, además de las que nosotros le queramos añadir. Por tanto, JavaEjemplo1 es una extensión o una generalización de la clase Activity. Se dice que Activity es una super-clase de JaveEjemplo1.
   La siguente línea:
     /** Called when the activity is first created. */
es un comentario. Los comentarios pueden abarcar varias líneas y se delimitan por las parejas de caracteres:
     /**  ...  */
o también:
     /*       */
Las dobles barras // comienzan un comentario hasta el final de la línea.
   Dentro de la definición de la clase JavaEjemplo1 encontramos la declaración de un método de la clase llamado onCreate.
        @Override
        public void onCreate(Bundle savedInstanceState) {
        }
Este método es una función con un parnámetro llamado savedInstanceState de tipo Bundle. La declaración comienza con la clave @Override, lo que indica que se está redefiniendo o sobreescribiendo el método onCreate de la super-clase. El tipo de acceso del método es public, o público, al que se puede acceder externamente desde otro programa. El método es de tipo void porque esta función no devuelve ningún resultado.
   Finalmente, entre dos llaves, tenemos la definición del método, que consiste en dos instrucciones:
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
En la pimera, el prefijo super indica ejecutar un método de la super-clase Activity. Por tanto, la instrucción:
   super.onCreate(savedInstanceState)  
ejecuta el método onCreate() de Activity, aplicado sobre el argumento savedInstanceState. La última instrucción ejecuta el método setContentView() tomando como argumento la variable R.layout.main, que es una referencia al fichero main.xml que contiene el layout de la interfaz de usuario que vemos en la pantalla.
Esta es la estructura básica de una actividad o programa de Android, que puede considerarse un molde, o template para todos los programas que escribiremos a continuación. Usaremos siempre el siguiente fichero res/layout/main.xml:
   <?xml version="1.0" encoding="utf-8"?>
   <LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
       android:background="#ffffff"
       android:orientation="vertical"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       >
   <TextView
       android:textColor="#000000"
       android:textSize="18sp"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:text="@string/hello"
       android:id="@+id/textView"
       />
   </LinearLayout>
El anterior layout define un campo de texto llamado "textView" que nos permite escribir en la pantalla. A continuación, modificaremos el programa básico para escribir texto en la pantalla como en el siguiente ejemplo.
   package es.amaro.ugr.ejemplojava1;
   import android.app.Activity;
   import android.os.Bundle;
   import android.widget.TextView;
   public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
      TextView textView=
                      (TextView) findViewById(R.id.textView);
      textView.setText(
             "El programa más básico de Java con Android " +
             "escribe un texto en la pantalla con setText.");
      textView.append(
                    " Así se añade más texto con append.");
      textView.append("\nAsí se escribe una segunda línea." );
    }
}
Aquí se define una variable textView que se refiere al campo de texto y permite modificarlo con setText () y añadir más texto con append (). El resultado se ve en la figura A.1.
Figura A.1. Programa básico para escribir texto.
A.2. Variables
Las variables almacenan datos, que pueden ser datos primitivos (números en distintas representaciones) o referencias a objetos (que son conjuntos de datos). Los tipos de datos primitivos son: int, float, double, char, boolean, long, short y byte. Los más importantes pueden almacenar:
   int. Un número entero entre -2,147,483,648 y +2,147,483,647.
   float. Un número con decimales entre 3.4e-38 y 3.4e+38.
   double. Un número en doble precisión entre 1.7e-308 y 1.7e+308.
   char. Un caracter UNICODE.
   boolean. Una variable lógica que vale true o false.
Otros tipos de variables importantes son:
   String. Es una clase para almacenar cadenas de caracteres.
   void.     Se usa para describir un método que no devuelve ningún valor.
Las variables deben declararse e inicializarse en cualquier punto del programa. Por ejemplo, modifiquemos el programa anterior para declarar e inicializar algunas variables, que escribiremos en pantalla.
  public class EjemploJava1 extends Activity {
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText(
             "Declaración e inicialización de variables ");
         // declaración e inicialización de variables
         int i;
         i=123;
         float x,y=1;
         x=0.5123f;
         double a= 123, b=3.1416e-10;
         char caracter=‘a’;
         boolean esFalso=true;
         String cadena="Esto es una cadena";
         // escribe los valores de las variables
         tv.append("\n i="+i);
         tv.append("\n x="+x+",y="+y);
         tv.append("\n a="+a+",b="+b);
         tv.append("\n caracter="+caracter);
         tv.append("\n esFalso="+esFalso);
         tv.append("\n cadena="+cadena);
     }
 }
El resultado de este programa se ve en la figura A.2. Vemos que pueden declararse varias variables del mismo tipo en la misma sentencia separándolas con comas. Los valores de las variables float deben tener el sufijo "f".
Figura A.2. Declaración e inicialización de variables.
A.3. Conversión de variables
Las valores de las variables se pueden convertir de un tipo a otro. Si la conversión se hace a un tipo más amplio, por ejemplo de int a float, la conversión se realiza implícitamente. Si, al contrario, la conversión se realiza a un tipo más restringido, necesitamos indicarlo explícitamente con una conversión o cast, indicando el nuevo tipo entre paréntesis pues se puede perder información. Por ejemplo:  
   float a = 1.5f;
   int i = (int) a;
En el siguiente ejemplo convertimos un valor de int a float a double y viceversa.
   public class EjemploJava1 extends Activity {
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
       TextView tv=(TextView) findViewById(R.id.textView);
       tv.setText(
          "Conversión de variables ");
       // conversion de int a float a double
       int i=125;
       float x=i;
       double d=x;
       tv.append("\n\n Conversion de int a float a double");
       tv.append("\n i="+i);
       tv.append("\n x="+x);
       tv.append("\n d="+d);
       // conversion de double a float a int
       d=0.0123456789e3;
       x=(float) d;
       i=(int) x;
       tv.append("\n\n Conversion de double a float a int");
       tv.append("\n d="+d);
       tv.append("\n x="+x);
       tv.append("\n i="+i);
       }
   }
El resultado de la conversión se ve en la figura A.3
Figura A.3. Conversión de variables.
A.4. Operaciones con variables
Con las variables numéricas se pueden realizar las operaciones aritméticas de suma, resta, multiplicación y división (+,-,*,/). También está definido el resto tras una división (%) y el incremento y decremento en una unidad (++, --).  A continuación, tenemos un ejemplo de utilización de estos operadores algebraicos. El resultado se ve en la figura A.4. Los operadores se pueden combinar para realizar operaciones más complejas. Hay que tener cuidado de incluir los paréntesis necesarios, si no estamos seguros de la preferencia en que se realizan las operaciones. Por ejemplo, la multiplicación se realiza antes que la suma, así que x+y*z=x+(y*z). Se pueden sumar variables de distinto tipo y el resultado es una variable del tipo más amplio. Por ejemplo, la suma de un entero y un float es un float. Si queremos que el resultado de una operación se convierta a un tipo más restingido, debemos preceder la expresión con el nuevo tipo entre paréntesis, poniendo también entre paréntesis, si es necesario, la expresión que se está convirtiendo. Por ejemplo i=(int)(x+y) no es lo mismo que i=(int)x+y. En este último caso, x se convierte a entero antes de sumar. Ante la duda siempre es preferible escribir los paréntesis.
Figura A.4. Operaciones algebraicas con variables.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText(" Operaciones aritméticas\n");
         int i=1,j;
         double x=2,y=3,z;
         // suma
         z=x+y;
         tv.append("\n "+x+" + "+y+" = "+z);
         // resta
         z=x-y;
         tv.append("\n "+x+" - "+y+" = "+z);
         // multiplicacion
         z=x * y;
         tv.append("\n "+x+" * "+y+" = "+z);
         // division
         z=x / y;
         tv.append("\n "+x+" / "+y+" = "+z);
         // resto
         x= 74;
         z = x % y;
         tv.append("\n\n "+x+" / "+y+" da resto = "+z);
         // incremento
         i++;
         tv.append("\n\n i incrementado en uno = "+i);
         i--;
         tv.append("\n i decrementado en uno ="+i);
         // suma de tipos distintos
         tv.append("\n\n Operaciones con tipos distintos");
         z= x+i;
         j= (int) x+i;
         tv.append("\n"+x+"+"+i+"="+z);
         tv.append("\n"+x+"+"+i+"="+j);
         // operaciones más complejas
         z= (x*(y+j)/(x*x+1)-1/y)*(i-x)/y;
         z = z*z;
         tv.append(
            "\n\n Resultado de operación compuesta z="+z);
         }
 }
A.5. Funciones matemáticas
Las funciones matemáticas están definidas como métodos de la clase Math en el paquete java.lang. Este paquete es importado automáticamente. En el siguiente ejemplo utilizamos algunas de las funciones más usuales y el resultado se ve en la figura A.5.
Figura A.5. Funciones matemáticas en Java.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         double x,y,z;
         x=Math.PI;
         tv.setText("x = PI = "+x);
         y=-1;
         tv.append("\n y = "+y);
         tv.append("\n Valor absoluto de y ="+Math.abs(y));
         z=Math.sqrt(x);
         tv.append("\n Raiz cuadr. de x="+z);
         z=Math.log(x);
         tv.append("\n Logaritmo de x ="+z);
         z=Math.exp(x);
         tv.append("\n Exponencial(x) ="+z);
         z=Math.pow(x,3);
         tv.append("\n x al cubo ="+z);
         z=Math.cos(x);
         tv.append("\n coseno de x= "+z);
         z=Math.sin(x);
         tv.append("\n seno de x= "+z);
         z=Math.tan(x);
         tv.append("\n Tangente(x)= "+z);
         z=Math.acos(y);
         tv.append("\n Arcocoseno(y)= "+z);
         z=Math.asin(y);
         tv.append("\n Arcoseno(y)= "+z);
         z=Math.atan(y);
         tv.append("\n Arcotangente(y)= "+z);
         z=Math.max(x,y);
         tv.append("\n Maximo de x e y ="+z);
         z=Math.IEEEremainder(x, y);
         tv.append("\n El resto de x entre y es= "+z);
         z=Math.toDegrees(x);
         tv.append("\n Expresado en grados x= "+z);
         z=Math.toRadians(y);
         tv.append("\n Expresado en radianes y="+z);
     }
 }
Otros métodos de interés son:
   random() devuelve un número aleatorio entre 0 y 1,
   los métodos de redondeo hacia arriba ceil(), hacia abajo floor(), que proporcionan números double con cifras decimales nulas,
   round() redondea un float y lo transforma en el int más próximo, o redondea un double y lo transforma en long.
El siguiente ejemplo hace uso de estos métodos. El resultado se ve en la figura A.6.
Figura A.6. Números aleatorios y métodos de redondeo.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Números aleatorios y métodos de redondeo");
         double x,y;
         int i;
         x=2*Math.random();
         tv.append(
             "\n Un número aleatorio entre 0 y 2, \n x= "+x);
         y=Math.ceil(x);
         tv.append("\n ceil(x)= "+y);
         y=Math.floor(x);
         tv.append("\n floor(x)= "+y);
         i=(int) Math.round(x);
         tv.append("\n round(x)= "+i);
         tv.append(
             "\n\n Redondeos hasta cualquier cifra decimal");
         y=Math.round(x*1.e2)/1.e2;
         tv.append("\n redondeado a la segunda cifra  "+y);
         y=Math.round(x*1.e3)/1.e3;
         tv.append("\n redondeado a la tercera cifra  "+y);
         y=Math.round(x*1.e4)/1.e4;
         tv.append("\n redondeado a la cuarta cifra  "+y);
         y=Math.round(x*1.e5)/1.e5;
         tv.append("\n redondeado a la quinta cifra  "+y);
     }
 }
A.6. Bloque if-else
Un bloque es una serie de instrucciones entre los caracteres abrir y cerrar llave:
{ ... }
En Java un bloque se procesa como si se tratara de una sóla instrucción o sentencia. Las variables declaradas dentro de un bloque no están definidas fuera de él.
Los bloques if-else permiten ejecutar instrucciones dependiendo de la relación entre los  valores de ciertas variables. Esta relación se establece mediante el uso de los operadores de comparación, que son:  igual, distinto, mayor, menor, mayor o igual, menor o igual:
==, !=, >, <, >=, <=
También se pueden utilizar variables booleanas, que darán true o false si cierta relación se verifica, por ejemplo:
   float x = 1, y = 2 ;
   boolean condicion = x < y;
Los operadores lógicos entre variables booleanas son: AND, denotado en java:
   & , &&
y OR:
   | ,  ||
Por ejemplo:
   boolean condicion1,condicion2,condicion3,condicion4;
   condicion3 = condicion1 & condicion2;
   condicion4 = condicion1 | condicion2;
La diferencia entre los operadores simples (&) o dobles (&&), es que el simple evalúa ambas condiciones, mientras que el doble evalúa la primera y, si es falsa, no evalúa la segunda.
En el siguiente ejemplo demostramos el uso del bloque if-else y del uso de variables boolean. El resultado en la figura A.7.
Figura A.7. Bloques if-else y uso de variables booleanas.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText(" Bloque if-else \n");
         double x=2,y=3;
         if(x==y){
         tv.append("\n "+x+" = "+y);
         } else if(x>y){
         tv.append("\n "+x+" > "+y);
         } else {
         tv.append("\n "+x+" < "+y);
         }
         tv.append("\n\n Operadores booleanos");
         boolean condicion1= x>1;
         boolean condicion2 = y<5;
         tv.append("\n condicion1 es = "+condicion1);
         tv.append("\n condicion2 es = "+condicion2);
         if(condicion1 & condicion2){
          tv.append("\n Las dos son true simultaneamente: " +
                "\n"+x+" > 1 y "+y+" < 10");
         } else{
         tv.append("\n Una de las dos es falsa");
         }
         }
 }
A.7. Bucles for
El bucle for permite repetir una instrucción normalmente utilizando un índice que se incrementa en cada paso. La instrucción a repetir puede ser una única  sentencia o un bloque delimitado por llaves. Su estructura es la siguiente:
   for ( inicializacion ; condicion ; incremento )
   {
    // bloque de sentencias
    sentencia1;
    sentencia2;
    ...
   }
El argumento de for, entre paréntesis, consta de tres sentencias separadas por un punto y coma. En la primera se inicializa una o varias variables. La segunda es una condición o expresión booleana. Si la condición se verifica, se ejecutará el bloque que viene a continuación. Finalmente se incrementa la variable. Se repite el proceso hasta que la condición deje de verificarse. Las variables declaradas dentro del bucle no están definidas fuera de él.
En el siguiente ejemplo usamos un bucle for para generar una tabla de valores de la función seno entre 0 y 1, figura A.8. Si intentamos escribir el valor de la variable i después del bucle, el compilador de java genera un error: variable no definida.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Bucle for");
         double x=10,y = 0;
         for(int i=0; i<10; i++){
          y=Math.sin(i/x);
          tv.append("\n "+i+", sin(i/x)= "+y);
         }
         tv.append(
              "\n Fin del bucle for, i no está definida");
         tv.append("\n y= "+y);
     }
 }
Figura A.8. Bucle for.
A.8. Bucle while
El bucle while es similar al bloque for, pero sólo requiere como argumento una condición o variable booleana. El bucle se ejecuta repetidamente hasta que la condición sea falsa.
   while( boolean )
   {
    // bloque de sentencias
    sentencia1;
    sentencia2;
    ...
   }
En el siguiente ejemplo generamos una tabla de 10 números aleatorios usando un bucle while con una variable que se va incrementando hasta que toma el valor 10, y el bucle finaliza (figura A.9).
Figura A.9. Bucle while.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Bucle while");
         int i=0;
         while(i<10){
          // variable no definida fuera del bucle
          double y=Math.random();
          tv.append("\n "+i+", random()= "+y);
          i++;
         }
         tv.append("\n Fin del bucle for, i="+i);
     }
 }
Para mayor control del desarrollo de un bucle, pueden utilizarse además las siguientes sentencias:
   break. Finaliza el buble.
   continue. Vuelve al comienzo del bucle.
Por ejemplo en el siguiente programa utilizamos break y continue para controlar la terminación de un bucle, saltando el quinto paso (figura A.10).
Figura A.10. Bucle while usando break y continue.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Bucle while");
         int i=0;
         while(true){
          i++;
          // salta al principio del bucle
          if(i==5) continue;
          // finaliza el bucle
          if(i==10) break;
          double y=Math.sqrt(i);
          tv.append("\n "+i+", sqrt()= "+y);
         }
         tv.append("\n Fin del bucle, i="+i);
     }
 }
A.9. Bloques switch
El bloque switch es una alternativa al bloque if-else si queremos realizar una acción dependiendo del valor de una variable, comparándola con una serie de casos. Después de cada caso hay que utilizar break para finalizar el bloque, porque todo lo que viene a continuación se ejecuta siempre sin realizar las comparaciones. He aquí un ejemplo de uso de switch dentro de un bloque for, cuyo resultado se ve en la figura A.11.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Bucle switch");
         for(int i=0;i<20;i++){
          switch(i){
          case 5:{
             tv.append("\n pasa por 5");
             break; }
          case 10: {
             tv.append("\n tambien pasa por 10");
             break; }
          case 15:{
             tv.append("\n toma el valor 15");
             break; }
          default:
             tv.append("\n i= "+i);
             }   // fin del bloque switch
         }  // fin del bloque for
     }
 }
Figura A.11. Bloque switch dentro de un bucle for.
A.10. Métodos
Un método es un bloque de sentencias que se puede ejecutar repetidas veces invocando su nombre. Los métodos pueden tener cero, uno o varios parámetros y devolver o no un resultado de cierto tipo. Los métodos son el análogo de las funciones o subrutinas de otros lenguajes. La declaración de un método tiene la siguiente estructura:
   acceso resultado nombre (lista de parámetros) {
   // Bloque del método
   ....
   return valor;  // si no es void
   }  
   Acceso indica si el método se puede ejecutar desde otra clase distinta, y puede ser public, private, protected, o el tipo por defecto, si no se indica nada.
   Resultado debe ser un tipo de dato primitivo, int, float, double, etc., o el nombre de una clase o void, si el método no devuelve ningún resultado.
   Nombre es el nombre del método.
   La lista de parámetros, indicando tipo y nombre, separados por comas.
En el siguiente ejemplo definimos un método para calcular el factorial de un número (n!). Este método lo incluimos como método de la clase EjemploJava1 después del método onCreate, en el que hemos estado programando hasta ahora. El método factorial es llamado repetidas veces en un bucle desde el método onCreate. Distintos métodos de una clase pueden llamarse entre sí. El resultado es una tabla con el factorial de los primeros 20 números, figura A.12.
Figura A.12. Uso de un método para calcular el factorial de un número.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Metodo factorial");
         for(int i=0;i<20;i++){
          tv.append("\n "+i+" ! = "+factorial(i));
         }
     }
     //  metodo para calcular el factorial de un numero
     double factorial(int n){
       double fac=1;
       if (n==0)return fac;
       for (int i=1;i<=n;i++)fac=fac*i;
       return fac;
     }
 }
En el siguiente ejemplo definimos un método void para imprimir un número, que no devuelve ningún resultado. Tiene dos argumentos: un número float y un objeto TextView, el campo de texto donde estamos escribiendo. El resultado de imprimir varios números en un bucle se ve en la figura A.13.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Metodo void para imprimir");
         for(int i=0;i<20;i++){
          float x=i*i;
          print(x,tv);
          }
     }
     // metodo para escribir un numero en un campo de texto
     void print(float x, TextView tv){
       tv.append("\n Metodo print "+x);
     }
 }
Figura A.13. Un método void para imprimir un número.
Un ejemplo de un método que no requiere ningún argumento es la función para generar números aleatorios Math.random().
A.11. Clases y objetos
Un objeto es un conjunto de datos de distintos tipos que se referencian con una única variable. Los objetos no sólo contienen datos sino que también realizan acciones. Cada objeto pertenece a una clase y debe declararse como tal precediendo el nombre de la variable por el nombre de la clase mediante la sentencia:
NombreDeLaClase variable;
Una clase es un bloque de código con toda la información sobre los tipos de datos que contienen sus objetos, cómo construirlos, y con métodos para operar sobre sus datos o para realizar acciones concretas. Una clase contiene variables de clase y métodos. Todos los métodos de una clase tienen acceso a las variables de clase y pueden modificarlas, a no ser que las variables se declaren final. La estructura de una clase es la siguiente:
   acceso class Nombre {
   // variables de la clase
   variable1;
   variable2;
   //otras variables
   ...
   //metodos de la clase
   metodo1 (parametros) {
   ....
   }
   metodo2 (parametros){
   ...
   }
   // otros metodos
   ...
   }
    
El identificador de acceso de una clase sólo puede ser public o ninguno. Todas las sentencias de un programa Java, excepto package e import, deben estar contenidas dentro de una clase. Las clases utilizadas en un programa Java pueden ser definidas por el programador, o ser de un paquete de las librerías de Java. Existe el convenio de que el nombre de una clase empiece siempre por mayúscula.
Un programa Java es una colección de clases en uno o varios ficheros. Un fichero puede contener varias clases, pero sólo una puede ser public. El nombre del fichero debe coincidir con el de la única clase public que contiene.
En el siguiente ejemplo definimos una clase Dato del tipo más simple, que contiene únicamente datos: un entero, un float y un double.  En la clase principal EjemploJava1 creamos un objeto de tipo Dato usando new Dato() y asignamos valores a sus variables o campos de clase. Para acceder a estas variables usamos el nombre del objeto, seguido de un punto y el nombre de la variable. La salida del programa se ve en la figura A.14. La clase Dato se ha incluido al final del fichero principal, pero podría igualmente incluirse sola en un archivo Dato.java en el mismo directorio, declarándola public.
Figura A.14. Uso de una clase para albergar datos.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText(
                  "Clase que contiene tres datos numéricos");
         tv.append("\n Se accede a las variables de clase " +
             "mediante el nombre del objeto, seguido de un " +
             "punto y el nombre de la variable");
         // declaracion de objeto Dato
         Dato miDato;
         // creación de objeto Dato;
         miDato=new Dato();
         // asignación de valores para el objeto Dato
         miDato.i=1;
         miDato.f=1.35f;
         miDato.d=2.3e2;
         tv.append("\n miDato.i= "+miDato.i);
         tv.append("\n miDato.f = "+miDato.f);
         tv.append("\n miDato.d = "+miDato.d);
     }
 }
   class Dato {
     int i;
     float f;
     double d;
   }
Entre los programadores de Java, se aconseja que las variables de clase se declaren privadas mediante private y que no puedan ser modificadas, ni siquiera visibles, fuera de la clase. Esto significa que el acceso a la variable de clase mediante objeto.variable no es posible y dará un error de compilación. Para extraer y modificar las variables se usan exclusivamente métodos, lo que permite controlar sus posibles valores. Para inicializar las variables se usa también un método constructor con el mismo nombre que la clase, que se ejecuta al crear un nuevo objeto. El constructor debe ser void. Puede haber varios constructores con distintos argumentos y se ejecutará el que corresponda.
Figura A.15. Uso de una clase para albergar datos, con variables de clase privadas.
A continuación, modificamos el ejemplo anterior ocultando los campos de clase y usando métodos. El constructor Dato() permite inicializar los objetos al crearlos (figura A.15).
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText(
                  "Clase que contiene tres datos numéricos");
         tv.append("\n Se accede a las variables de clase " +
             "mediante el nombre del objeto, seguido de un " +
             "punto y uno de los métodos get()");
         // declaracion de objeto Dato
         Dato miDato;
         // creación de objeto Dato;
         miDato=new Dato(1,1.35f,2.3e2);
         tv.append("\n miDato.getI()= "+miDato.getI());
         tv.append("\n miDato.getF() = "+miDato.getF());
         tv.append("\n miDato.getD() = "+miDato.getD());
         miDato.setI(200);
         miDato.setF(1.23456f);
         miDato.setD(0.1111e-23);
         tv.append(
          "\n\n Después de modificar las variables con SET:");
         tv.append("\n miDato.getI()= "+miDato.getI());
         tv.append("\n miDato.getF() = "+miDato.getF());
         tv.append("\n miDato.getD() = "+miDato.getD());
     }
 }
    
   class Dato {
     private int i;
     private float f;
     private double d;
   // metodo constructor de la clase
   Dato(int ivar, float fvar, double dvar){
     i=ivar;
     f=fvar;
     d=dvar;
   }
   // metodos SET para modificar las variables
   void setI(int ivar){
     i=ivar;
   }
   void setF(float fvar){
     f=fvar;
   }
   void setD(double dvar){
     d=dvar;
   }
   // metodos GET para extraer las variables
   int getI(){
     return i;
   }
   float getF(){
     return f;
   }
   double getD(){
     return d;
   }
  }
En el siguiente ejemplo creamos una clase de números complejos llamada Complejo, que se ha añadido al final del fichero. Esta clase contiene dos variables a,b que almacenarán la parte real e imaginaria, respectivamente. La clase contiene cuatro métodos. Uno con el mismo nombre de la clase, Complejo, que se llama constructor de la clase, que se ejecuta al crear un objeto de la clase, donde se inicializan las variables. Los otros tres métodos extraen la parte real, imaginaria y el módulo del número complejo representado por un objeto. En este ejemplo creamos dos números complejos y escribimos sus partes real e imaginaria. El resultado se ve en la figura A.16.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Clase de números Complejos");
         Complejo c1,c2,c3;
         c1=new Complejo(1,1);
         c2=new Complejo(2,3);
         // partes real e imaginaria usando métodos
         tv.append("\n\n c1="+c1.real()+"+i"+c1.imag());
         tv.append("\n c2="+c2.real()+"+i"+c2.imag());
         // partes real e imaginaria usando variables de clase
         tv.append("\n\n c1="+c1.a+"+i"+c1.b);
         tv.append("\n c2="+c2.a+"+i"+c2.b);
         tv.append("\n\n modulo de c1 = "+c1.modulo());
         tv.append("\n modulo de c2 = "+c2.modulo());
     }
 }
   class Complejo {
     double a,b;
     Complejo(double x,double y){
        a=x;
        b=y;
     }
     double real(){
        return a;
     }
     double imag(){
        return b;
     }
     double modulo(){
        return Math.sqrt(a*a+b*b);
     }
   }
Figura A.16. Uso de la clase de números complejos.
La instrucción para crear el número complejo 2+3i usando esta clase es:
Complejo c = new Complejo(2,3);
En el ejemplo vemos que podemos acceder a las variables o a los métodos de un objeto utilizando el nombre del objeto seguido de un punto y el nombre de la variable, o el nombre del método. Por ejemplo, la parte real del número anterior se puede extraer de estas dos formas:
double r;
r=c.real();
r=c.a;
A.12. Sub-clases
En Java se puede definir una subclase, que hereda las variables y métodos de la clase original, llamada su superclase. La subclase puede contener variables y métodos nuevos, o puede redefinir o sobreescribir los métodos de su superclase. Para declarar una subclase se usa la estructura:
class Subclase extends Superclase{
}
Esta estructura de subclase es ya familiar para nosotros, puesto que la hemos encontrado en todos los ejemplos anteriores, al definir la clase EjemploJava1 como una subclase de la clase Activity. También hemos visto que en todos los ejemplos se define el método onCreate(), precedido por la etiqueta @Override.  Esto indica que estamos redefiniendo, sobreescribiéndolo, el método con el mismo nombre de la superclase Activity.
En el siguiente ejemplo definimos una subclase Complejo2 de la clase Complejo del ejemplo anterior. Esta subclase define nuevos métodos para sumar, multiplicar e invertir números complejos. Las variables y métodos de la superclase están definidos y se pueden invocar dentro de la nueva clase usando el prefijo this. La clase y la superclase están aquí en el mismo fichero, pero podrían haberse puesto en ficheros java distintos, con el mismo nombre que la clase correspondiente.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Ejemplo de una subclase");
         tv.append(
           "\n La clase Complejo2 extiende la clase Complejo"+
             " definiendo métodos para la suma y producto " +
             " de números complejos");
         Complejo2 c1,c2,c3,c1Inv,c2Inv;
         c1= new Complejo2(1,1);
         c2= new Complejo2(2,3);
         tv.append("\n c1="+c1.real()+" + i"+c1.imag());
         tv.append("\n c2="+c2.real()+" + i"+c2.imag());
         // suma c1+c2
         c3=c1.sum(c2);
         tv.append("\n suma ="+c3.real()+" + i"+c3.imag());
         // producto c1*c2
         c3=c1.prod(c2);
         tv.append(
               "\n producto ="+c3.real()+" + i"+c3.imag());
         // inverso 1/c1
         c1Inv=c1.inv();
         tv.append(
          "\n 1/c1 ="+c1Inv.real()+" + i("+c1Inv.imag()+")");
         // inverso 1/c2
         c2Inv=c2.inv();
         tv.append(
          "\n 1/c2 ="+c2Inv.real()+" + i("+c2Inv.imag()+")");
         // producto c1*1/c1
         c3=c1.prod(c1Inv);
         tv.append("\n c1/c1 ="+c3.real()+" + i"+c3.imag());
         // producto c2*1/c2
         c3=c2.prod(c2Inv);
         tv.append("\n c2/c2 ="+c3.real()+" + i"+c3.imag());
     }
 }
   class Complejo2 extends Complejo{
     Complejo2(double x,double y){
        super(x,y);
     }
     Complejo2 sum(Complejo2 c){
        double cReal=this.real()+c.real();
        double cImag=this.imag()+c.imag();
        Complejo2 result= new Complejo2(cReal,cImag);
        return result;
     }
     Complejo2 prod(Complejo2 c){
        double cReal=this.real()*c.real()-this.imag()*c.imag();
        double cImag=this.real()*c.imag()+this.imag()*c.real();
        Complejo2 result= new Complejo2(cReal,cImag);
        return result;
     }
     Complejo2 inv(){
        double modulo=this.modulo();
        double cReal=this.real()/(modulo*modulo);
        double cImag=-this.imag()/(modulo*modulo);
        Complejo2 result= new Complejo2(cReal,cImag);
        return result;
     }
    }
     
    class Complejo {
     double a,b;
     Complejo(double x,double y){
        a=x;
        b=y;
     }
     double real(){
        return a;
     }
     double imag(){
        return b;
     }
     double modulo(){
        return Math.sqrt(a*a+b*b);
     }
  }
El resultado se muestra en la figura A.17. En este ejemplo vemos que el constructor de la subclase consiste en la línea super(x,y), lo que indica que se ejecute el constructor de la superclase Complejo(x,y). La variable super hace siempre referencia a la superclase de la clase actual. La misma fórmula se emplea al definir el método onCreate(), cuya primera línea es super.onCreate(), para ejecutar el método onCreate() de la superclase Activity.
Figura A.17. Uso de una subclase de números complejos.
Obsérvese que en los tres métodos de Complejo2 se usa la variable especial this, para indicar el nombre del objeto asociado a la clase actual. Así, this.real() sería la parte real del presente objeto.
A.13. Variables y métodos estáticos y finales
Una clase puede contener variables estáticas, que deben ser declaradas como variables de clase mediante:
static variable;
No se pueden definir dentro de un método. Estas variables pertenecen a la clase y no requieren un objeto de la clase para usarlas, aunque un objeto puede modificarlas. Para acceder a una variable estática se precede su nombre por el nombre de la clase, como Clase.variable. Por ejemplo, la variable Math.PI de la clase de funciones matemáticas es estática.
Una clase puede contener también métodos estáticos, que no requieren un objeto de la clase para utilizarlos. Se emplean de la misma forma que las variables estáticas. Por ejemplo, los métodos de la clase de funciones matemáticas Math, como Math.log(), son estáticos.
Las variables finales son constantes que se inicializan cuando se declaran y no se pueden modificar. Se suelen escribir con mayúsculas. Por ejemplo:
final double PI=3.1416;
Una variable final también puede declararse estática.
   static final PI=3.1416;
La variable Math.PI es estática y final.
Un método también puede declararse final. Se prohibe entonces que el método pueda ser modificado por una subclase. En cierto modo el método es constante, como las variables finales.
En el siguiente ejemplo definimos una clase con tres variables y un método estáticos. Dos de las variables son final y no pueden modificarse, pero la variable aa es incrementada cada vez que se construye un objeto. Esta variable vale inicialmente cero (inicializada al compilar) y puede ser referenciada antes de que exista ningún objeto de la clase. Al crear un nuevo objeto, con new Aa(), la variable se incrementa en una unidad, y puede utilizarse para contar el número de objetos de la clase que se han creado.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv=(TextView) findViewById(R.id.textView);
         tv.setText("Variables y métodos final y/o  static");
         tv.append("\n static aa="+Abc.aa);
         tv.append("\n static final BB="+Abc.BB);
         tv.append("\n static final CC="+Abc.CC);
         double x=1.5;
         tv.append("\n Funcion static f(x)="+Abc.f(x));
         for (int i=1;i<10;i++){
         Abc v1=new Abc();
         v1.write(tv);
         }
         tv.append("\n Fuera del bucle:");
         Abc v2=new Abc();
         v2.write(tv);
         Abc v3=v2;
         v3.write(tv);
     }
 }
   class Abc{
     static int aa=0;
     static final int BB=555;
     static final int CC=777;
     Abc(){
        aa++;
     }
     void write(TextView tv){
        tv.append("\n Variable static aa="+aa);
     }
     static double f(double x){
        return BB+CC*x;
     }
   }
La salida de este programa se ve en la figura A.18. Obsérvese que el valor de la variable estatica aa se incrementa cada vez que se crea un objeto nuevo en el bucle, pero no al asignar la última variable v3, puesto que no se crea ningún objeto nuevo, sólo se asigna una referencia a un objeto que ya existe.
Figura A.18. Uso de variables y métodos static.
A.14. Arrays
Los arrays consisten en grupos de variables que se referencian con uno o varios índices enteros. El tamaño del array, es decir, el número de objetos que contiene, debe especificarse al crearlo. Un array es a su vez un objeto de una clase especial y debe crearse con la orden new, seguida de la clase a la que pertenencen los objetos del array, especificando su longitud entre corchetes. Por ejemplo, para declarar un array conteniendo 3 números enteros usamos:
   int [] a;
   a = new int[3];
Equivalentemente, se puede declarar un array escribiendo los corchetes tras el nombre de la variable. Por ejemplo:
   int a[];
El array anterior contiene tres variables enteras denotadas a[0], a[1], a[2]. Los contenidos del array se denotan poniendo entre corchetes una variable entera, que cuenta el número de orden de cada elemento, a[i]. Pero hay que tener siempre en cuenta que se empieza a contar en cero. Es decir el primer elemento de un array es el número cero, y si el array tiene dimensión n, su último elemento es el n-1. Un array es un objeto y length es la variable de clase donde se almacena su longitud.  Por tanto, la longitud del array anterior se puede obtener mediante a.length.
Los contenidos de un array se inicializan automáticamente a cero si son numéricos. También podemos inicializar un array igualándolo a un bloque de valores separados por comas, como:
   int[] a = {1,2,3,4,5};
En el siguiente ejemplo ilustramos el uso de los arrays en un programa Java.  Definimos un array entero, un array double, y una función cuyo argumento es un array. El resultado se ve en la figura A.19..
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv= (TextView) findViewById(R.id.textView);
         tv.setText("Ejemplos de arrays");
         // definicion de un array de longitud 3
         int[] miArray;
         miArray=new int[3];
         // obtención de la longitud del array
         int longitud=miArray.length;
         tv.append("\n Longitud del array= "+longitud);
         // escribimos los valores iniciales del array (cero)
         for (int i=0;i<3;i++){
          tv.append("\n i="+i+", miArray[i]="+miArray[i]);
          miArray[i]=1+i*i;
          tv.append("\n Valor después ="+miArray[i]);
          }
         // inicializacion de un array
         double[] miArray2={0,1,2,3};
         tv.append("\n miArray2=");
         for (int i=0;i<miArray2.length;i++){
          tv.append(" "+i+" , ");
          }
          double total=suma(miArray2);
          tv.append("\n suma="+total);
     }
     // suma los elementos de un array
     double suma(double[] a){
        double s=0;
        int nsumandos=a.length;
        for(int i=0;i<nsumandos;i++){
          s= s+a[i];
        }
        return s;
     }
 }
Figura A.19. Uso de arrays.
A.15. Arrays 2D
Un array de dos dimensiones (2D) es un array de arrays. Se especifica con dos índices entre corchetes. El primero indica la fila y el segundo la columna. Por ejemplo un array de enteros con dos filas y tres columnas se declara de la siguiente forma:
   int[][] a;
   a= new int[2][3];
y sus elementos son variables enteras denotadas a[i][j], donde i=0,1 y j=0,1,2.
También se puede definir cada fila separadamente como un array de una dimensión. Por ejemplo, en las siguientes líneas primero declaramos un array 2D que tiene dos filas. A continuación, se declara que cada fila es a su vez un array con tres elementos:
   int[][] a = new int[2][];
   a[0]=new int[3];
   a[1]=new int[3];
Esto permite construir arrays con número de columnas variable. Por ejemplo, la primera fila podría contener dos elementos y la segunda cuatro, de la siguiente forma:
   int[][] a = new int[2][];
   a[0]=new int[2];
   a[1]=new int[4];
El siguiente programa Android es una demostración del uso de arrays 2D en Java, con número de columnas fijo y variable. El resultado se puede ver en la figura A.20..
Figura A.20. Uso de arrays 2D.
  public class EjemploJava1 extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView tv= (TextView) findViewById(R.id.textView);
         tv.setText("Ejemplos de arrays");
         // definicion de un array bidimensional
         int[][] miArray;
         miArray=new int[3][2];
         // obtención de la longitud del array
         int filas=miArray.length;
         int columnas=miArray[0].length;
         tv.append("\n miArray \n filas = "+filas);
         tv.append(", Columnas= "+columnas);
         // inicializamos el array
         for (int i=0;i<filas;i++){
          for (int j=0;j<columnas;j++){
             miArray[i][j]=10*i+j;
          }
         }
         // escribe el contenido del array
         for (int i=0;i<filas;i++){
          tv.append("\n fila: ");
          for (int j=0;j<columnas;j++){
             tv.append(" "+miArray[i][j]+",");
          }
         }
         // definimos e incializamos un array
         double miArray2[][]={ {1,2,3}, {4,5,6}};
         filas=miArray2.length;
         columnas=miArray2[0].length;
         tv.append("\n\n miArray2:");
         tv.append("\n Filas= "+filas);
         tv.append(" , Columnas = "+columnas);
         // escribe el contenido del array
         for (int i=0;i<filas;i++){
          tv.append("\n fila: ");
          for (int j=0;j<columnas;j++){
             tv.append(" "+miArray2[i][j]+",");
          }
         }
         // array con columnas variables
         double miArray3[][]=new double[3][];
         miArray3[0]=new double[1];
         miArray3[1]=new double[2];
         miArray3[2]=new double[3];
         miArray3[0][0]=0;
         miArray3[1][0]=10;
         miArray3[1][1]=11;
         miArray3[2][0]=20;
         miArray3[2][1]=21;
         miArray3[2][2]=22;
         tv.append("\n\n miArray3:");
         for (int i=0;i<3;i++){
         tv.append("\n Fila: ");
          for(int j=0;j<=i;j++){
             tv.append(""+miArray3[i][j]+" , ");
          }
         }
         double[][] miArray4={ {1,2},{3,4,5},{6},{7,8,9,0} };
         tv.append("\n\n miArray4: ");
         filas=miArray4.length;
         for (int i=0;i<filas;i++){
         columnas=miArray4[i].length;
         tv.append("\n Fila:");
         for(int j=0;j<columnas;j++){
            tv.append(" "+miArray4[i][j]+" , ");
         }
      }
    }
 }
A.16. Cadenas
La clase String está definida en el paquete java.lang y representa cadenas de caracteres. Una cadena o String es un objeto de esta clase. Una String literal es un texto rodeado por comillas dobles. Para crear un objeto String se puede usar el constructor de la clase o usar una cadena literal, por ejemplo:
   String cadena;
   cadena = new String("esto es una cadena");
   cadena = "esto es una cadena";
Para transformar a String una variable numérica, booleana o carácter, se usa el método estático valueOf().
   double x=1.245;
   String cadena= String.valueOf(x);
Para concatenar cadenas se usa el operador "+".
   String cadena1= "esto es ";
   String cadena2= "una cadena";
   String cadena3= cadena1+cadena2;
El operador "+" también realiza la conversión a cadenas de otros tipos de datos.
   double x=1.256;
   String cadena1= "x="+x;
Este hecho lo hemos utilizado repetidamente en todos los ejemplos anteriores, pues el argumento de append() es una String.
Java incluye numerosos métodos para manipular cadenas incluyendo:
   length(). La longitud de una cadena cadena1 se extrae mediante cadena1.length().
   substring(int ini,int fin). Método para extraer una subcadena. Se especifica la posición del primer caracter incluido y del primer caracter no incluido. La posición del primer caracter tiene el índice cero.
   toUpperCase() y toLowerCase(). Conversión a mayúsculas y a minúsculas.
   compareTo(String cadena). Compara con otra cadena y devuelve un entero negativo si la primera es menor que la segunda, y positivo si ocurre lo contrario. Si son iguales devuelve cero.
   indexOf(string cadena).   Devuelve   la   posición   de   la   primera ocurrencia de una subcadena.
   replace(char a, char b). Reemplaza el primer caracter por el segundo.
   trim(). Elimina espacios en blanco al principio y al final.
   toCharArray(). Extrae los caracteres de una cadena en un array.
El uso de estos métodos se ilustra en el siguiente programa. La salida se puede ver en la figura A.21..
  public class EjemploJava1 extends Activity {
    TextView tv;    
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         String cadena1="Cadenas de caracteres";
         tv.setText(cadena1);
         // constructor
         String cadena2=new String("\n Constructor de cadenas");
         tv.append(cadena2);
         // Concatenacion
         cadena1="\n Concatenacion";
         cadena2=" de cadenas \n";
         String cadena3=cadena1+cadena2;
         tv.append(cadena3);
         // Transformacion a cadena con valueOf()
         double x=3.1416e-3;
         cadena2 = String.valueOf(x);
         tv.append("Transformacion de double a cadena\n");
         tv.append(cadena2);
         // conversion de String a double
         cadena1="3.1416e-3";
         x = Double.parseDouble(cadena1);
         x= x*2;
         tv.append("\n Conversion de string a double\n "+x);
         // conversion de String a int
         cadena1="1234";
         int i= Integer.parseInt(cadena1);
         i=i*2;
         tv.append("\n Conversion de string a int\n "+i);
         // longitud de una cadena
         cadena1="Granada";
         int longitud= cadena1.length();
         tv.append("\n La longitud de Granada es "+longitud);
         longitud="Nueva York".length();
         tv.append(
              "\n La longitud de Nueva York es "+longitud);
         // conversion a mayusculas y minusculas
         cadena1="Conversion a mayusculas";
         cadena1=cadena1.toUpperCase();
         tv.append("\n"+cadena1);
         cadena1="CONVERSION A MINUSCULAS";
         cadena1=cadena1.toLowerCase();
         tv.append("\n"+cadena1);
         // subcadenas. Indices comienzan en cero
         cadena1="Extrayendo subcadenas";
         // desde el principio hasta el caracter decimo
         cadena2=cadena1.substring(0,10);
         // desde el 11 hasta el final
         cadena3=cadena1.substring(11);
         tv.append("\n"+cadena2);
         tv.append("\n"+cadena3);
         // comparacion de cadenas
         cadena1="Granada";
         cadena2="Albacete";
         i=cadena1.compareTo(cadena2);
         if( i<1){
          tv.append("\n"+cadena1+" antes que "+cadena2);
          }
         else{
          tv.append("\n"+cadena1+" despues que "+cadena2);
          }
         // búsqueda de subcadenas
         cadena1="Granada";
         i=cadena1.indexOf("da");
         tv.append("\n Granada contiene da en la posicion "+i);
         // reemplazar caracteres
         cadena1="Granada";
         cadena2=cadena1.replace(‘a’,’u’);
         tv.append(
             "\n Reemplazando las aes por ues: "+cadena2);
         //elimina espacios en blanco
         cadena1=
            "    trim elimina blancos  al inicio y fin     ";
         cadena1=cadena1.trim();
         tv.append("\n*"+cadena1+"*");
          
         // extraccion de los caracteres
         cadena1="Granada";
         char[] letras=cadena1.toCharArray();
         tv.append("\n Separando los caracteres:\n");
         for (int j=0;j<letras.length;j++){
          tv.append(" + "+letras[j]);
         }
     }
 }
Figura A.21. Manipulación de cadenas.
A.17. Formato numérico
Java posee una completa librería para manipular cadenas. En esta sección introducimos la clase DecimalFormat para dar formato a números decimales, transformándolos en cadenas. Esta clase es de utilidad para mostrar números en pantalla con una estructura de cifras determinada, o para escribirlos en un fichero. Para ello, primero se construye un objeto de tipo DecimalFormat:
DecimalFormat df = new DecimalFormat(patron)
donde patron es una cadena indicando el número de cifras deseado, o formato. Luego se emplea el método format() para transformar un número en una cadena con dicho formato. Por ejemplo:
   double x=1.2345;
   String cadena = df.format(x);
El número se redondea hacia arriba. Las principales reglas para especificar el patrón son las siguientes:
   Un punto indica la posición del punto decimal.
   El símbolo 0 indica un dígito o un cero si no hay ningún dígito en esa posición. Por ejemplo, el patrón "00.000" indica números con tres decimales y con dos cifras como mínimo a la izquierda del punto decimal, por ejemplo, 12.345, 01.234, 12.340, 123.450.
   El símbolo # indica un dígito. Si no hay ningún dígito en esa posición, no escribe nada. Por ejemplo, el patrón "##.###" produce números con el formato 12.345, 1.23, 123.45.
   El símbolo E seguido de ceros se utiliza para notación científica e indica una potencia de 10. Por ejemplo, el patrón "0.00E00" produciría los números 1.23E03, 0.12E10, 1.20E-01.
   Una coma indica agrupación de cifras con un separador, Por ejemplo, el patron ",000" agrupa por miles, como en 12,000.
   Con un punto y coma se puede especificar un símbolo alternativo al signo menos de los números negativos, en lugar de un guión.
Por defecto se utiliza la notación "local" que generalmente consiste en que el punto decimal es una coma y el separador de cifras es un punto. Para modificar el separador es necesario especificar un objeto DecimalFormatSymbols, que define los separadores, al definir el formato. Por ejemplo, para que el punto decimal se escriba siempre con un punto:
DecimalFormatSymbols symbols= new
                            DecimalFormatSymbols(Locale.US);
symbols.setDecimalSeparator(‘.’);
df= new DecimalFormat(".###",symbols);  
El objeto symbols lleva los símbolos locales de Estados Unidos, pero hemos modificado su separador decimal con setDecimalSeparator().
En el siguiente programa mostramos varios ejemplos de formato. El resultado se puede ver en la figura A.22..
  import java.text.DecimalFormat;
  import java.text.DecimalFormatSymbols;
  import java.util.Locale;
  import android.app.Activity;
  import android.os.Bundle;
  import android.widget.TextView;
  public class EjemploJava1 extends Activity {
    TextView tv;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         tv.setText("DecimalFormat");
         double x,y,z;
         x=12345.6789;
         tv.append("\n Sin formato x="+x);
         DecimalFormat df1;
         df1= new DecimalFormat(".#");
         tv.append("\n Una cifra decimal: "+df1.format(x));
         df1= new DecimalFormat(".##");
         tv.append("\n Dos cifras decimales: "+df1.format(x));
         df1= new DecimalFormat(".###");
         tv.append(
              "\n Tres cifras decimales: "+df1.format(x));
         df1= new DecimalFormat(",###.##");
         tv.append("\n Agrupar por miles: "+df1.format(x));
         DecimalFormatSymbols symbols =
                 new DecimalFormatSymbols(Locale.US);
         symbols.setDecimalSeparator(‘.’);
         df1= new DecimalFormat(".###",symbols);
         tv.append("\n\n Punto decimal = ."+df1.format(x));
         y=1.234;
         z=-1.234;
         tv.append(
           "\n\n Rellenar con ceros a derecha e izquierda:");
         tv.append("\n sin formato y="+y);
         tv.append("\n sin formato z="+z);
         // El ; permite definir el simbolo del signo menos
         df1= new DecimalFormat("00.0000;menos",symbols);
         tv.append("\n "+df1.format(y));
         tv.append("\n "+df1.format(z));
         tv.append("\n\n Notación científica");
         df1= new DecimalFormat("0E0",symbols);
         tv.append("\n "+df1.format(y));
         df1= new DecimalFormat("0.0E00",symbols);
         tv.append("\n "+df1.format(y));
         df1= new DecimalFormat("00.00##E00",symbols);
         tv.append("\n "+df1.format(y));
         df1= new DecimalFormat(".00E0",symbols);
         tv.append("\n "+df1.format(y));
     }
 }
Figura A.22. Uso de la clase DecimalFormat para dar formato a los números.
A.18. Manejo de Excepciones
Cuando en la ejecución de un programa se produce un error, ésta se detiene. Cada error suele producir un mensaje o excepción, que permite determinar el problema. Con el manejo de excepciones podemos evitar errores y que el programa siga ejecutándose. Para manejar una excepción se usa un bloque try-catch. El código donde se puede producir un error debe estar en el bloque try. Si el error se produce, se lanza una excepción y se ejecuta el código del bloque catch, que es el que recoge la excepcion. Opcionalmente se puede incluir un bloque finally, que se ejecuta en todo caso, se produzca la excepción o no.
   try {
   // codigo que arroja la excepcion
   }
   catch (Exception e){
   // codigo que recoge la excepcion
   }
   finally {
   // codigo que se ejecuta siempre
   }
La instrucción catch requiere un parámetro e, que es un objeto de tipo  Exception o una de sus subclases, que lleva la información del tipo de error producido. En el siguiente programa demostramos el manejo de excepciones forzando dos errores típicos: intentar acceder a índices de un array que no existen, lo que arroja una excepción del tipo ArrayIndexOutOfBoundException, y acceder a índices de una cadena que tampoco existen, lo que arroja una excepción de tipo StringIndexOutOfBoundException. El tipo de excepción recogido se escribe en pantalla, ver figura A.23..
  public class EjemploJava1 extends Activity {
    TextView tv;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         tv.setText("Excepciones\n");
         double[] v =new double[10];
         double x=-1;
         try {
          x=v[10];
         } catch(Exception e){
          tv.append("\n\n Excepcion encontrada: "+e);
          tv.append(
                "\n\n Se ha intentado acceder al indice 10 "
                +"del array v[10]");
         } finally{
          tv.append("\n x="+x);
         }
         String cadena="abcdefg";
         try{
          tv.append("\n"+cadena.substring(15,20));
         } catch(Exception e){
         tv.append("\n\n Excepción encontrada: "+e);
         tv.append("\n\n Se ha intentado acceder " +
                "a la posicion 15 de  "+cadena);
         }
     }
  }
Figura A.23. Uso de bloques try-catch para recoger excepciones.
Finalmente, indiquemos que un método puede arrojar una excepción y ésta ser declarada mediante la etiqueta throws, por ejemplo:
   public metodo throws Excepcion {
      ...
      throw e;
   }
En el caso en que se produzca la excepción, el método debe arrojarla mediante la instrucción throw e.
A.19. Interfaces
En Java un método no puede ser el argumento de otro método. Por ejemplo, si escribimos un método para calcular el mínimo de una función F(x), el nombre de la función debe conocerse de antemano. Si queremos minimizar varias funciones distintas, esto podría ser un problema. Las interfaces resuelven este problema, definiendo métodos vacíos, que se pueden implementar posteriormente por distintas clases. Así una clase podría contener la función a minimizar y podríamos pasarle un objeto de esa clase al método minimizador. Una interfaz se define similarmente a una clase, sólo que sus métodos sólo están declarados, es decir, no están implementados. Por lo tanto, las interfaces no son clases y, por tanto, no se pueden instanciar objetos de una interfaz, sino de una clase que implemente la interfaz.
Por ejemplo, para definir una interfaz que lleva una función, escribiríamos:
   interface Funcion{
       double f(double x);
   }
Aquí, la función f(x) está declarada, pero no está implementada. Una clase que implementa esta interfaz podría ser la siguiente:
   class Funcion1 implements Funcion{
         public double f(double x){
           return 1+x+2*x*x;
         }
   }
y un método que utiliza la interfaz sería:
   double evalua(double x, Funcion portador){
     double valor=portador.f(x);
     return valor;
   }
Nótese que aquí el argumento no es un objeto de la clase Funcion, sino de una clase que implemente dicha interfaz.
El siguiente ejemplo es un programa completo donde se implementa la interfaz anterior tres veces, con tres funciones distintas, que se evalúan en un punto y se muestra el resultado (ver figura A.24.).
  public class EjemploJava1 extends Activity {
    TextView tv;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         tv.setText("Ejemplo de interfaz " +
             "\n\n Se implementan tres funciones distintas");
         double a=0.5;
         class Funcion1 implements Funcion{
           public double f(double x){
           return 1+x+2*x*x;
           }
         }
         Funcion1 miF1 = new Funcion1();
         double valor=evalua(a, miF1);
         tv.append("\n\n Valor de miF1 = "+ valor);
         class Funcion2 implements Funcion{
           public double f(double x){
           return 1/(1+2*x*x);
           }
         }
         Funcion2 miF2 = new Funcion2();
         valor=evalua(a, miF2);
         tv.append("\n\n Valor de miF2 = "+ valor);
         class Funcion3 implements Funcion{
           public double f(double x){
           return Math.atan(x);
           }
         }
         Funcion3 miF3 = new Funcion3();
         valor=evalua(a, miF3);
         tv.append("\n\n Valor de miF3 = "+ valor);
     }
     // metodo para evaluar una funcion
     // proporcionada en la interfaz Funcion
     double evalua(double x, Funcion portador){
       double valor=portador.f(x);
       return valor;
    }
}
  // interfaz que lleva una funcion
  interface Funcion{
    double f(double x);
 }
    double evalua(double x, Funcion portador){
       double valor=portador.f(x);
       return valor;
    }
}
  // interfaz que lleva una funcion
  interface Funcion{
    double f(double x);
  }
Figura A.24. Uso de una interfaz para evaluar distintas funciones en un método.
Otro ejemplo del uso de interfaces es el siguiente. Trabajando con arrays 2D o matrices encontramos a menudo bloques comunes del tipo:
   for (int i=0;i<miArray.length;i++){
     for(int j=0;j<miArray[i].length;j++){
        // instrucciones
         ...
      }
   }
El bloque común del doble bucle podría pasarse a un método recorreArray(), cuyo argumento serían las instrucciones a realizar. Esto puede hacerse con una interfaz con un método manipula, como en el siguiente programa. La interfaz, Manipulador, está implementada por dos clases y los objetos de esas clases se pasan al método recorreArray()que contiene el bucle. El resultado se ve en la figura A.25..
Figura A.25. Uso de una interfaz para manipular matrices.
  public class EjemploJava1 extends Activity {
    TextView tv;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         tv.setText("Ejemplo de interfaz");
         // array bidimensional
         double[][] miArray;
         miArray=new double[4][3];
         class Mani1 implements  Manipulador{
           public void manipula(double[][] a,int i, int j){
              a[i][j]=10*i+j;
           }
         }
         Mani1 m1= new Mani1();
         recorreArray(miArray,m1);
         class Mani2 implements  Manipulador{
           public void manipula(double[][] a,int i, int j){
              tv.append("\n a("+i+","+j+") = "+a[i][j]);
           }
         }
         Mani2 m2= new Mani2();
         recorreArray(miArray,m2);
     }
     // metodo para realizar acciones sobre un array
     // usando la interfaz Manipulador
     void recorreArray(double[][] miArray, Manipulador m){
       for (int i=0;i<miArray.length;i++){
         for(int j=0;j<miArray[i].length;j++){
              m.manipula(miArray,i,j);
               }
            }
         }
  }
   // interfaz para manipular un array
   interface Manipulador{
         void manipula(double[][] a,int i,int j);
   }
Las clases internas Mani1 y Mani2 que implementan la interfaz Manipulador son ejemplos de clases locales, que sólo son visibles dentro de un bloque de código. En este caso, sólo se necesita instanciar un objeto de estas clases, por lo que esta implementación se podría haber hecho utilizando clases anónimas, tratadas en la siguiente sección.
A.20. Clases anónimas
Cuando sólo se necesita un objeto de la clase, por ejemplo para implementar una interfaz, no es realmente necesario definir una clase completa. Se puede utilizar una clase anónima, es decir, que no tiene nombre, instanciando directamente un objeto de la clase. Por ejemplo:
   Interfaz inter= new Interfaz(){
   // implementacion
   ...
   };
Nótese que aquí no se está creando un objeto de la clase Interfaz, porque las interfaces no son clases, sino que se está creando un objeto de una clase anónima que no tiene nombre y que implementa la interfaz.
En el siguiente programa implementamos la interfaz Funcion de la sección anterior usando clases anónimas, el resultado es exactamente el mismo de la figura A.24.
  public class EjemploJava1 extends Activity {
    TextView tv;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         tv.setText("Ejemplo de interfaz " +
             "\n\n Se implementan tres funciones distintas");
         double a=0.5;
         Funcion miF1= new Funcion(){
           public double f(double x){
              return 1+x+2*x*x;
           }
         };
         double valor=evalua(a, miF1);
         tv.append("\n\n Valor de miF1 = "+ valor);
         Funcion miF2 = new Funcion(){
           public double f(double x){
              return 1/(1+2*x*x);
           }
         };
         valor=evalua(a, miF2);
         tv.append("\n\n Valor de miF2 = "+ valor);
         Funcion miF3 = new Funcion(){
           public double f(double x){
              return Math.atan(x);
           }
         };
          
         valor=evalua(a, miF3);
         tv.append("\n\n Valor de miF3 = "+ valor);
     }
     // metodo para evaluar una funcion
     // proporcionada en la interfaz Funcion
    double evalua(double x, Funcion portador){
       double valor=portador.f(x);
       return valor;
    }
  }
   // interfaz que lleva una funcion
   interface Funcion{
     double f(double x);
   }
Como descubrimos al examinar este ejemplo, en realidad tampoco es necesario darle nombre a los objetos miF1, miF2, miF3 de la clase anónima que implementa la interfaz Funcion, sino que bastaría incluir su definición directamente como argumento del método evalua(), es decir:
   double valor=evalua(a, new Funcion(){
        public double f(double x){
           return 1+x+2*x*x;
        }
   });
Obtenemos, entonces, la siguiente versión equivalente del mismo programa que ilustra las posibilidades que ofrece el lenguaje Java para definir un método, dentro del argumento de otro método.
  public class EjemploJava1 extends Activity {
    TextView tv;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         tv.setText("Ejemplo de interfaz " +
             "\n\n Se implementan tres funciones distintas");
         double a=0.5;
         double valor=evalua(a, new Funcion(){
           public double f(double x){
              return 1+x+2*x*x;
           }
         });
         tv.append("\n\n Valor de miF1 = "+ valor);
         valor=evalua(a, new Funcion(){
           public double f(double x){
              return 1/(1+2*x*x);
           }
         });
         tv.append("\n\n Valor de miF2 = "+ valor);
         valor=evalua(a, new Funcion(){
           public double f(double x){
              return Math.atan(x);
           }
         });
         tv.append("\n\n Valor de miF3 = "+ valor);
     }
     // metodo para evaluar una funcion
     // proporcionada en la interfaz Funcion
    double evalua(double x, Funcion portador){
       double valor=portador.f(x);
       return valor;
   }
  }
    // interfaz que lleva una funcion
    interface Funcion{
      double f(double x);
    }
Aunque esta notación es compacta y es utilizada por muchos programadores, implementar una interfaz con una clase anónima dentro del argumento de un método puede resultar confuso para el principiante, pues el código resulta más difícil de entender. Sólo con la práctica se llega a dominar esta técnica. Para comenzar se aconseja implementar explícitamente las interfaces con clases no anónimas. La utilidad de la clase anónima surgirá por sí sola.
Para terminar esta discusión, el siguiente ejemplo muestra la versión del programa de la figura A.25. implementado con clases anónimas.
  public class EjemploJava1 extends Activity {
     TextView tv;
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         tv= (TextView) findViewById(R.id.textView);
         tv.setText("Ejemplo de interfaz");
         // array bidimensional
         double[][] miArray;
         miArray=new double[4][3];
         // Llamada al método con segundo argumento un objeto
         // de una clase anónima implementando una interfaz
         recorreArray(miArray,new Manipulador(){
           public void manipula(double[][] a, int i, int j) {
                   a[i][j]=10*i+j;
          }
         });
         recorreArray(miArray,new Manipulador(){
          public void manipula(double[][] a,int i, int j){
             tv.append("\n a("+i+","+j+") = "+a[i][j]);
          }
         });
     }
     // metodo para realizar acciones sobre un array
     // usando la interfaz Manipulador
    void recorreArray(double[][] miArray, Manipulador m){
       for (int i=0;i<miArray.length;i++){
          for(int j=0;j<miArray[i].length;j++){
             m.manipula(miArray,i,j);
          }
       }
    }
  }
    // interfaz para manipular un array
    interface Manipulador{
      void manipula(double[][] a,int i,int j);
    }
A.21. Otras características de Java
A.21.1. Paquetes
En Java las clases se agrupan en paquetes, especificados en la primera línea del fichero:
   package es.ugr.amaro;
Esto indica que el fichero .class, obtenido al compilar el fichero .java, está localizado en el subdirectorio es/ugr/amaro. Todas las clases del mismo paquete están en el mismo directorio. Para evitar problemas, se aconseja utilizar nombres únicos para los paquetes, como un nombre de dominio o de correo electrónico.  Para que una clase pueda utilizar otra clase definida en otro paquete se debe especificar ésta con la orden import al principio del fichero. Por ejemplo, para usar la clase EjemploJava1 del paquete es.ugr.amaro, escribiríamos:
   import es.ugr.amaro.EjemploJava1;
Se pueden importar todas las clase de un paquete escribiendo un asterisco:
   import es.ugr.amaro.*
A.21.2. Clases públicas
Por defecto, las clases son privadas y sólo son accesibles a las otras clases de su mismo paquete. Si queremos que una clase sea pública y pueda ser importada por clases en otros paquetes, debemos declararla como public. Por ejemplo:
   public class EjemploJava1{
   ...
   }
A.21.3. Privilegios de acceso de los métodos y variables
Aunque una clase sea pública, sus métodos y variables tienen su propio tipo de acceso, que puede ser de cuatro tipos: public, protected, private, o el tipo por defecto, si no se especifica nada. La diferencia está en el tipo de clases que pueden acceder a ellos.
   public: es el tipo menos restrictivo, permitiendo acceso a todas las clases. Es decir, si un método es public, puede invocarse desde cualquier clase de cualquier paquete.
   protected: no permite el acceso a las clases de otro paquete, a no ser que sean sub-clases de la clase que contiene el método o variable.
   tipo por defecto: si no se especifica nada, no permite el acceso a las clases de otro paquete. Dicho de otro modo, los métodos no pueden ser ejecutados por clases de otro paquete. Si queremos que lo sean, deben ser declarados públicos.
   private: es el tipo más rectictivo. No es accesible ni siquiera a las clases del mismo paquete. Sólo lo puede ejecutar la clase que lo contiene.
A.21.4. Clases y métodos abstractos
Una clase abstracta se declara precediéndola del comando abstract. Las clases abstractas son similares a las interfaces, conteniendo lo que se denomina métodos abstractos, precedidos también de la palabra abstract, y que sólo están declarados, pero no implementados. Las clases abstractas se diferencian de las interfaces en que también pueden contener métodos concretos, es decir, implementados. No se puede crear un objeto de una clase abstracta, sino sólo de una de sus subclases que implemente todos sus métodos abstractos.