Curso de Programación Básico Contenido 1. Introducción 2. Características de Java 3. Tipos de datos 3.1 Enteros 3.2 Reales 3.3 Caracteres 3.4 Boolean 4. Cadenas de Caracteres 5. Definición de Variables 6. Operadores 7.Estructura de clases 7.1 Declaración de la clase 7.2 El cuerpo de la clase 7.3 Declaración de atributos 7.4 Tipos de definiciones de las clases 7.5 Private, protected o public 8. Estructura de los métodos 9. Estructuras de control 9.1 if…[else] 9.2 Switch…case…brake…default . 9.3 While 9.4 Do…while 9.5 For www.javarevolutions.com Canal YouTube: Java Revolutions 9.6 Break y continue 10. Objetos y herencia 10.1 Creación y referencia a objetos 10.2 this 10.3 super 10.4 interfaces www.javarevolutions.com Canal YouTube: Java Revolutions 1. INTRODUCCIÓN Comenzamos con una serie de notas sobre Java, especialmente para aquellos que quieren comenzar a conocerlo y usarlo. La idea es dar una guía ordenada para el estudio de este lenguaje, muy poderoso y de gran coherencia, aunque todavía adolece de algunas limitaciones que seguramente se irán superando con el tiempo. Java es un lenguaje originalmente desarrollado por un grupo de ingenieros de Sun, utilizado por Netscape posteriormente como base para Javascript. Si bien su uso se destaca en el Web, sirve para crear todo tipo de aplicaciones (locales, intranet o internet). 2. CARACTERÍSTICAS DE JAVA A continuación haremos una pequeña redacción de las características del lenguaje, que nos ayudarán a ver para que tipo de problemas está pensado Java: 2.1 Simple Es un lenguaje sencillo de aprender. Su sintaxis es la de C++ “simplificada”. Los creadores de Java partieron de la sintaxis de C++ y trataron de eliminar de este todo lo que resultase complicado o fuente de errores en este lenguaje. La herencia múltiple, la aritmética de punteros, por la gestión de memoria dinámica (que en Java se elimina de modo transparente para el programador gracias al recogedor basura) son ejemplos de "tareas complicadas" de C++ y en Java se han eliminado poco simplificado. 2.2 Orientado a Objetos Posiblemente sea el lenguaje más orientado a objetos de todos los existentes; en Java todo, a excepción de los tipos fundamentales de variables (int, char, long...) es un objeto. 2.3 Distribuido Java está muy orientado al trabajo en red, soportando protocolos como TCP/IP, UDP, HTTP y FTP. Por otro lado el uso de estos protocolos es bastante sencillo comparandolo con otros lenguajes que los soportan. 2.4 Robusto www.javarevolutions.com Canal YouTube: Java Revolutions El compilador Java detecta muchos errores que otros compiladores solo detectarían en tiempo de ejecución o incluso nunca. A esclarecer así por ejemplo " if(a=b) then ... " o " int i; h=i*2; " son dos ejemplos en los que el compilador Java no nos dejaría compilar este código; sin embargo un compilador C compilaría el código y generaría un ejecutable que ejecutaría esta sentencia sin dar ningún tipo de error). 2.5 Seguro Sobre todo un tipo de desarrollo: los Applet. Estos son programas diseñados para ser ejecutados en una página web. Java garantiza que ningún Applet puede escribir o leer de nuestro disco o mandar información del usuario que accede a la página a través de la red (como, por ejemplo, la dirección de correo electrónico). En general no permite realizar cualquier acción que pudiera dañar la máquina o violar la intimidad del que visita la página web. 2.6 Portable En Java no hay aspectos dependientes de la implementación, todas las implementaciones de Java siguen los mismos estándares en cuanto a tamaño y almacenamiento de los datos. Esto no ocurre así en C++, por ejemplo. En éste un entero, por ejemplo, puede tener un tamaño de 16, 32 o más bits, siendo lo única limitación que el entero sea mayor que un short y menor que un long int. Así mismo C++ bajo UNIX almacena los datos en formato little endian, mientas que bajo Windows lo hace en big endian. Java lo hace siempre en little edian para evitar confusiones. 2.7 Arquitectura Neutral El código generado por el compilador Java es independiente de la arquitectura: podría ejecutarse en un entorno UNIX, Mac o Windows. El motivo de esto es que el que realmente ejecuta el código generado por el compilador no es el procesador del ordenador directamente, sino que este se ejecuta mediante una máquina virtual. Esto permite que los Applets de una web pueda ejecutarlos cualquier máquina que se conecte a ella independientemente de que sistema operativo emplee (siempre y cuando el ordenador en cuestión tenga instalada una máquina virtual de Java). 2.8 Multithread Soporta de modo nativo los threads, sin necesidad del uso de de librerías específicas (como es el caso de C++). Esto le permite además que cada Thread de una aplicación java pueda correr en www.javarevolutions.com Canal YouTube: Java Revolutions una CPU distinta, si la aplicación se ejecuta en una máquina que posee varias CPU. Las aplicaciones de C++ no son capaces de distribuir, de modo transparente para el programador, la carga entre varias CPU. 3. TIPOS DE DATOS En Java toda variable declarada ha de tener su tipo, y además antes de poder emplearla hemos de inicializarla a un valor, si no es compilador se quejará y no generará los archivos .class. Esto por ejemplo en C no es necesario, siendo fuente de muchos errores al emplearse en operaciones variables que nos hemos olvidado de inicializar. A continuación pasamos a describir los tipos de datos: 3.1 Enteros Almacenan como su propio nombre indica números enteros, sin parte decimal. Cabe destacar, como ya se indicó en el primer tema, que por razones de portabilidad todos los datos en Java tienen el mismo tamaño y formato. En Java hay cuatro tipos de enteros: Tabla 1: tipo de datos enteros en Java Tipo Tamaño (bytes) Rango Byte 1 -128 a 127 Short 2 -32768 a 32767 Int 4 -2147483648 a 2147483647 -9223372036854775808 a Long 8 9223372036854775807 Para indicar que una constante es de tipo long lo indicaremos con una L: 23L. www.javarevolutions.com Canal YouTube: Java Revolutions 3.2 Reales Almacenan número reales, es decir números con parte fraccionaria. Hay dos tipos: Tabla 2: tipos de datos reales en Java Tipo Tamaño (bytes) Rango Float 4 + 3.40282347E+38 Double 8 + 179769313486231570E+308 Si queremos indicar que una constante es flotante: ej: 2.3 hemos de escribir: 2.3F, sino por defecto será double. 3.3 Caracteres En Java hay un único tipo de carácter: char. Cada carácter en Java esta codificado en un formato denominado Unicode, en este formato cada carácter ocupa dos bytes, frente a la codificación en ASCII, dónde cada carácter ocupaba un solo byte. Unicode es una extensión de ASCII, ya que éste último al emplear un byte por carácter sólo daba acogida a 256 símbolos distintos. Para poder aceptar todos los alfabetos (chino, japonés, ruso...) y una mayor cantidad de símbolos se creó el formato Unicode. En Java al igual que en C se distingue la representación de los datos char frente a las cadenas de caracteres. Los char van entre comillas simples: char ch = ‘a’, mientras que las cadenas de caracteres usan comillas dobles. 3.4 Boolean www.javarevolutions.com Canal YouTube: Java Revolutions Se trata de un tipo de dato que solo puede tomar dos valores: “true” y “false”. Es un tipo de dato bastante útil a la hora de realizar chequeos sobre condiciones. En C no hay un dato equivalente y para suplir su ausencia muchas veces se emplean enteros con valor 1 si “true” y 0 si “false”. Otros lenguajes como Pascal sí tiene este tipo de dato. 4 CADENAS DE CARACTERES En Java no hay un tipo predefinido para cadenas de caracteres, en su lugar hay una clase, String, que es la que soporta las distintas operaciones con cadenas de caracteres. La definición de un String es: String e ; //no inicializado String e =””; //cadena vacía String e = “Hola”; //inicialización y asignación juntas. A continuación veremos algunas operaciones básicas soportadas por la clase String: 4.1 Concatenación La concatenación en Java es increíblemente sencilla: se realiza con el operador +, es decir “sumando” cadenas de caracteres obtenemos la concatenación de estas. Lo ilustraremos con un ejemplo: String saludo = “hola”; String nombre = “Pepe”; String saluda_pepe = “”; saluda_pepe = saludo + nombre;// saluda_pepe toma el valor holaPepe La sencillez de Java en el manejo de cadenas de caracteres llega incluso más alla: si una cadena la intentamos encadenar con otro tipo de variable automáticamente se convierte la otra variable a String, de tal modo que es perfectamente correcto: String saludo = “hola”; int n = 5; www.javarevolutions.com Canal YouTube: Java Revolutions saludo = saludo + “ “ + n;// saludo toma el valor “hola 5” 4.2 Subcadenas En la clase String hay un método que permite la extracción de una subcadena de caracteres de otra. Su sintaxis es: Nombre_String.substring((int)posición_inicial,(int)posición_final ); Donde posición_inicial y posición_final son respectivamente la posición del primer carácter que se desea extraer y del primer carácter que ya no se desea extraer. String saludo = “hola”; String subsaludo = “”; Subsaludo = saludo.substring(0,2);// subsaludo toma el valor “ho” Puede extraerse un char de una cadena, para ello se emplea el método charAt(posición), siendo posición la posición del carácter que se desea extraer. 4.3 Comparación de cadenas Se empleo otro método de String: equals. Su sintaxis es: cadena1.equals(cadena2); Devuelve true si son iguales y false si son distintos. El siguiente ejemplo permitirá ilustrar estas operaciones con Strings: www.javarevolutions.com Canal YouTube: Java Revolutions public class Ejemplo5 { public static void main(String[] args) { String saludo = "Hola"; String saludo2 ="hola"; int n = 5; //Imprime por consola la subcadena formada por los caracteres //comprendidos entre el caractero 0 de saludo y hasta el //carácter 2, sin incluir el último prt(saludo.substring(0,2)); //Concatena saludo con un espacio en blanco y con el valor de //la variable n prt(saludo +" " + n); //Imprime el resultado del test de igualdad entre saludo y //saludo2. Son distintos, en Java se distingue entre //mayúsculas y minúsculas. prt("saludo == saludo2 "+ saludo.equals(saludo2)); } static void prt(String s) { System.out.println(s); } } 5 DEFINICIÓN DE VARIABLES Al igual que C, Java requiere que se declaren los tipos de todas las variables empleadas. La sintaxis de iniciación es la misma que C: int i; Sin embargo, y a diferencia que en C, se requiere inicializar todas las variables antes de usarlas, si no el compilador genera un error y aborta la compilación. Se puede inicializar y asignar valor a una variable en una misma línea: www.javarevolutions.com Canal YouTube: Java Revolutions int i = 0; Asignación e inicialización pueden hacerse en líneas diferentes: int i ; i = 0; Es posible iniciar varias variables en una línea: int i, j,k; Después de cada línea de código, bien sea de iniciación o de código, al igual que en C va un ;. En Java, al igual que en todo lenguaje de programación hay una serie de palabras reservadas que no pueden ser empleadas como nombres de variables (if, int, char, else, goto....); alguna de estas se emplean en la sintaxis del lenguaje, otras, como goto no se emplean en la actualidad pero se han reservado por motivos de compatibilidad por si se emplean en el futuro. Los caracteres aceptados en el nombre de una variable son los comprendidos entre “A-Z”, “a-z”, _, $ y cualquier carácter que sea una letra en algún idioma. 6 OPERADORES Los operadores básicos de Java son + , - , * , / para suma, resta, producto y división. El operador / representa la división de enteros si ambos operandos son enteros. Su módulo puede obtenerse mediante el operador % (7/4= 1; 7% 4=3). Además existen los operadores decremento e incremento: -- y ++ respectivamente. La operación que realizan son incrementar y decrementar en una unidad a la variable a la que se aplican. Su acción es distinta según se apliquen antes (++a) o después (a++) de la variable. El siguiente programa ilustra estos distintos efectos: public class Ejemplo2{ public static void main(String[] args) { int i = 1; www.javarevolutions.com Canal YouTube: Java Revolutions prt("i : " + i); prt("++i : " + ++i); // Pre-incremento, primero //incrementa y luego imprime por consola prt("i++ : " + i++); // Post-incremento, primero imprime //“2” por consola y luego incrementa i. prt("i : " + i);//i por lo tanto vale 3 prt("--i : " + --i); // Pre-decremento, primero //decrementa i y luego lo imprime por consola prt("i-- : " + i--); // Post-decremento, primero imprime //i por consola y luego de decrementa. prt("i : " + i);//Ahora i vale 1 } //esto nos ahorrara teclear. Invocando a prt podremos //imprimir por consola la cadena de caracteres que le pasemos static void prt(String s) { System.out.println(s); } } 6.1 Exponenciación En Java a diferencia que en otros lenguajes no existe el operador exponenciación. Tampoco existen los operadores Seno o Coseno. Si se desea realizar una operación de exponenciación se deberá invocar el método correspondiente de la clase Math de Java.lang. Estos métodos son estáticos, por lo que no es necesario crear un objeto de dicha clase. Su sintaxis general es: Math.metodo(argumentos); En el siguiente código aparecen ejemplos. Si alguna vez deseamos realizar algún otro tipo de operación y queremos ver si la soporta Java podemos hacerlo consultando la ayuda on-line de la clase Math, o bien la documentación que podemos descargar los desde la misma página que él jdk. www.javarevolutions.com Canal YouTube: Java Revolutions public class Ejemplo3 { public static void main(String[] args) { int i = 45,j=2; //Imprime por consola la cadena de caracteres “Cos i : “ //concatenado con el resultado de calcular el coseno de i prt("Cos i : " + Math.cos(i)); prt("Sen i : " + Math.sin(i)); prt("j^i : " + Math.pow(j,i)); } } 6.2 Operadores lógicos En la tabla a continuación recogemos los operadores lógicos disponibles en Java: Tabla 3: operadores lógicos Operador Operación que realiza ! Not lógico == Test de igualdad != Test de desigualdad < Menor que > Mayor que <= Menor o igual que >= Mayor o igual que && And lógico || Or lógico www.javarevolutions.com Canal YouTube: Java Revolutions Con el siguiente ejemplo se muestra la función de estos operadores: import java.util.*; public class Ejemplo4 { public static void main(String[] args) { //Creamos un objeto de tipo Random, almecenado un puntero a //el en la variable rand. En el tema 5 se detallará como se //crean objetos. Random rand = new Random(); //el método nextInt() del objeto Random creado (se invoca //como rand.nextInt()) genera un número aleatorio entero. En //el tema 5 se explica que son los métodos y como emplearlos. //El módulo 100 de un entero aleatorio será un entero //aleatorio entre 0 y 100. int i = rand.nextInt() % 100; int j = rand.nextInt() % 100; //Imprime I y j por consola prt("i = " + i); prt("j = " + j); //Imprime diversas operaciones binarias sobre i y j, junto //con su resultado. prt("i > j es " + (i > j)); prt("i < j es " + (i < j)); prt("i >= j es " + (i >= j)); prt("i <= j es " + (i <= j)); prt("i == j es " + (i == j)); prt("i != j es " + (i != j)); prt("(i < 10) && (j < 10) es " + ((i < 10) && (j < 10)) ); prt("(i < 10) || (j < 10) es " + ((i < 10) || (j < 10)) ); www.javarevolutions.com Canal YouTube: Java Revolutions } static void prt(String s) { System.out.println(s); } } Una posible salida de este programa es: i = 85 j = 4 i > j es true i < j es false i >= j es true i <= j es false i == j es false i != j es true //true && false = false (i < 10) && (j < 10) es false //true || false = true (i < 10) || (j < 10) es true 7. ESTRUCTURA DE CLASES Vamos a comenzar analizando la clase Contador, para ir viendo las partes que forman una clase una por una y en detalle. Este capítulo va a ser un poco aburrido por lo exhaustivo (aunque algunos puntos más complicados como las excepciones y los threads los dejaremos para después), pero me parece bueno tener un resumen completo de la sintaxis desde ahora. Luego iremos armando pequeñas aplicaciones para probar cada cosa. Recordemos la definición de la clase Contador: // Implementación de un contador sencillo public class Contador { // Atributos int cnt; // Constructor public Contador() { www.javarevolutions.com Canal YouTube: Java Revolutions cnt = 0; } // Métodos public int incCuenta() { cnt++; return cnt; } public int getCuenta() { return cnt; } } 7.1 Declaración de la clase La clase se declara mediante la línea public class Contador. En el caso más general, la declaración de una clase puede contener los siguientes elementos: [public] [final | abstract] class Clase [extends ClaseMadre] [implements Interfase1 [, Interfase2 ]…] o bien, para interfaces: [public] interface Interfase [extends InterfaseMadre1 [, InterfaseMadre2 ]…] Como se ve, lo único obligatorio es class y el nombre de la clase. Las interfases son un caso de clase particular que veremos más adelante. 7.2 El cuerpo de la clase El cuerpo de la clase, encerrado entre { y }, es la lista de atributos (variables) y métodos (funciones) que constituyen la clase. No es obligatorio, pero en general se listan primero los atributos y luego los métodos. 7.3 Declaración de atributos En Java no hay variables globales; todas las variables se declaran dentro del cuerpo de la clase o dentro de un método. Las variables declaradas dentro de un método son locales al método; las www.javarevolutions.com Canal YouTube: Java Revolutions variables declaradas en el cuerpo de la clase se dice que son miembros de la clase y son accesibles por todos los métodos de la clase. Por otra parte, además de los atributos de la propia clase se puede acceder a todos los atributos de la clase de la que desciende; por ejemplo, cualquier clase que descienda de la clase Polygon hereda los atributos npoints, xpoints e ypoints. Finalmente, los atributos miembros de la clase pueden ser atributos de clase o atributos de instancia; se dice que son atributos de clase si se usa la palabra clave static: en ese caso la variable es única para todas las instancias (objetos) de la clase (ocupa un único lugar en memoria). Si no se usa static, el sistema crea un lugar nuevo para esa variable con cada instancia (o sea que es independiente para cada objeto). La declaración sigue siempre el mismo esquema: [private|protected|public] [static] [final] [transient] [volatile] Tipo NombreVariable [= Valor]; 7.4 Tipos de Definiciones de clases Definir una clase como pública (public) significa que puede ser usada por cualquier clase en cualquier paquete. Si no lo es, solamente puede ser utilizada por clases del mismo paquete (más sobre paquetes luego; básicamente, se trata de un grupo de clases e interfaces relacionadas, como los paquetes de biblioteca incluídos con Java). Una clase final (final) es aquella que no puede tener clases que la hereden. Esto se utiliza básicamente por razones de seguridad (para que una clase no pueda ser reemplazada por otra que la herede), o por diseño de la aplicación. Una clase abstracta (abstract) es una clase que puede tener herederas, pero no puede ser instanciada. Es, literalmente, abstracta (como la clase Number definida en java.lang). ¿Para qué sirve? Para modelar conceptos. Por ejemplo, la clase Number es una clase abstracta que representa cualquier tipo de números (y sus métodos no están implementados: son abstractos); las clases descendientes de ésta, como Integer o Float, sí implementan los métodos de la madre Number, y se pueden instanciar. www.javarevolutions.com Canal YouTube: Java Revolutions Por lo dicho, una clase no puede ser final y abstract a la vez (ya que la clase abstract requiere descendientes…) ¿Un poco complejo? Se va a entender mejor cuando veamos casos particulares, como las interfases (que por definición son abstractas ya que no implementan sus métodos). 7.5 Private, protected o public Java tiene 4 tipos de acceso diferente a las variables o métodos de una clase: privado, protegido, público o por paquete (si no se especifica nada). De acuerdo a la forma en que se especifica un atributo, objetos de otras clases tienen distintas posibilidades de accederlos: Acceso desde: private protected public package la propia clase S S S S subclase en el mismo paquete N S S S otras clases en el mismo paquete N S S S subclases en otros paquetes N X S N otras clases en otros paquetes N N S N S: puede acceder N: no puede acceder X: puede acceder al atributo en objetos que pertenezcan a la subclase, pero no en los que pertenecen a la clase madre. Es un caso especial ; más adelante veremos ejemplos de todo esto. 8 ESTRUCTURA DE LOS MÉTODOS Los métodos, como las clases, tienen una declaración y un cuerpo. www.javarevolutions.com Canal YouTube: Java Revolutions La declaración es del tipo: [private|protected|public] [static] [abstract] [final] [native] [synchronized] TipoDevuelto NombreMétodo ( [tipo1 nombre1[, tipo2 nombre2 ]…] ) [throws excepción1 [,excepción2]… ] A no preocuparse: poco a poco aclararemos todo con ejemplos. Básicamente, los métodos son como las funciones de C: implementan, a través de funciones, operaciones y estructuras de control, el cálculo de algún parámetro que es el que devuelven al objeto que los llama. Sólo pueden devolver un valor (del tipo TipoDevuelto), aunque pueden no devolver ninguno (en ese caso TipoDevuelto es void). Como ya veremos, el valor de retorno se especifica con la instrucción return, dentro del método. Los métodos pueden utilizar valores que les pasa el objeto que los llama (parámetros), indicados con tipo1 nombre1, tipo2 nombre2… en el esquema de la declaración. Estos parámetros pueden ser de cualquiera de los tipos ya vistos. Si son tipos básicos, el método recibe el valor del parámetro; si son arrays, clases o interfases, recibe un puntero a los datos (referencia). Veamos un pequeño ejemplo: public int AumentarCuenta(int cantidad) { cnt = cnt + cantidad; return cnt; } Este método, si lo agregamos a la clase Contador, le suma cantidad al acumulador cnt. En detalle: el método recibe un valor entero (cantidad) lo suma a la variable de instancia cnt devuelve la suma (return cnt) ¿Cómo hago si quiero devolver más de un valor? Por ejemplo, supongamos que queremos hacer un método dentro de una clase que devuelva la posición del mouse. Lo siguiente no sirve: www.javarevolutions.com Canal YouTube: Java Revolutions void GetMousePos(int x, int y) { x = ….; // esto no sirve! y = ….; // esto tampoco! } porque el método no puede modificar los parámetros x e y (que han sido pasados por valor, o sea que el método recibe el valor numérico pero no sabe adónde están las variables en memoria). La solución es utilizar, en lugar de tipos básicos, una clase: class MousePos { public int x, y; } y luego utilizar esa clase en nuestro método: void GetMousePos( MousePos m ) { m.x = ……; m.y = ……; } 8.1 El resto de la declaración Public, private y protected actúan exactamente igual para los métodos que para los atributos, así que veamos el resto. Los métodos estáticos (static), son, como los atributos, métodos de clase; si el método no es static es un método de instancia. El significado es el mismo que para los atributos: un método static es compartido por todas las instancias de la clase. Ya hemos hablado de las clases abstractas; los métodos abstractos (abstract) son aquellos de los que se da la declaración pero no la implementación (o sea que consiste sólo del encabezamiento). Cualquier clase que contenga al menos un método abstracto (o cuya clase madre contenga al menos un método abstracto que no esté implementado en la hija) es una clase abstracta. Es final un método que no puede ser redefinido por ningún descendiente de la clase. www.javarevolutions.com Canal YouTube: Java Revolutions Las clases native son aquellas que se implementan en otro lenguaje (por ejemplo C o C++) propio de la máquina. Sun aconseja utilizarlas bajo riesgo propio, ya que en realidad son ajenas al lenguaje. Pero la posibilidad de usar viejas bibliotecas que uno armó y no tiene ganas de reescribir existe!. Las clases synchronized permiten sincronizar varios threads para el caso en que dos o más accedan concurrentemente a los mismos datos. De nuevo, más detalles habrá en el futuro, cuando hablemos de threads. Finalmente, la cláusula throws sirve para indicar que la clase genera determinadas excepciones. También hablaremos de las excepciones más adelante. En la próxima veremos el cuerpo de la declaración y empezaremos a armar algunos ejemplos concretos para ir aclarando todos estos conceptos… 8.2 El cuerpo de los métodos Otra vez recordaremos nuestra vieja clase Contador: // Implementación de un contador sencillo public class Contador { public int incCuenta() { cnt++; return cnt; } } Dentro de los métodos pueden incluirse: Declaración de variables locales Asignaciones a variables Operaciones matemáticas Llamados a otros métodos: dentro de la clase www.javarevolutions.com Canal YouTube: Java Revolutions de instancia, de otras clases de clase, de cualquier clase Estructuras de control Excepciones (try, catch, que veremos más adelante) 8.3 Declaración de variables locales Las variables locales se declaran igual que los atributos de la clase: Tipo NombreVariable [= Valor]; Ej: int suma; float precio; Contador laCuenta; Sólo que aquí no se declaran private, public, etc., sino que las variables definidas dentro del método sólo son accesibles por él. Las variables pueden inicializarse al crearse: Ej: int suma = 0; float precio = 12.3; Contador laCuenta = new Contador ( ); 8.4 Asignaciones a variables Se asigna un valor a una variable mediante el signo =: www.javarevolutions.com Canal YouTube: Java Revolutions Variable = Constante | Expresión ; Ej: suma = suma + 1; precio = 1.05 * precio; laCuenta.cnt = 0; El último caso es válido si cnt es una variable pública de la clase Contador. Personalmente no creo conveniente acceder directamente a variables de otro objeto, ya que futuras modificaciones del objeto llamado o del que llama puede propender la difusión de errores… Es mejor usar métodos como getCuenta o un hipotético inicializarContador para ello. De hecho, algunos sugieren que todas las variables de una clase se declaren como private. En el primer caso, o sea en general: Variable = Variable Operador Expresión; se puede escribir en forma más sencilla: Variable Operador= Expresión; Por ejemplo, suma = suma + 9 - cantidad; puede escribirse: suma += 9-cantidad; y precio = precio * 0.97; como: precio *= 0.97; 8.5 Llamadas a métodos Se llama a un método de la misma clase simplemente con el nombre del método y los parámetros entre paréntesis, como se ve, entre otros, en el ejemplo en negrita: // Archivo: Complejo.java // Compilar con: javac Complejo.java public final class Complejo extends Number { www.javarevolutions.com Canal YouTube: Java Revolutions // atributos: private float x; private float y; // constructor: public Complejo(float rx, float iy) { x = rx; y = iy; } // métodos: public float Norma() { return (float)Math.sqrt(x*x+y*y); } // obligatorios (son abstractos en Number): public double doubleValue() { return (double)Norma( ); } public float floatValue() { return Norma(); } public int intValue() { return (int)Norma(); } public long longValue() { return (long)Norma(); } public String toString() { return "("+x+")+i("+y+")"; } } Pueden probar la clase (mínima) con el siguiente ejemplo de aplicación; la línea en negrita es un ejemplo de un llamado a un método de un objeto de otra clase. Notar que es este caso, es necesario llamar al método sobre un objeto (instancia) existente, por lo que se indica: www.javarevolutions.com Canal YouTube: Java Revolutions Nombre_del_Objeto<punto>Nombre_del_Método(parámetros) // Archivo: Ejemplo4.java // Compilar con: javac Ejemplo4.java // Ejecutar con: java Ejemplo4 import java.io.*; public class Ejemplo4 { public static void main(String args[]) { Complejo numComp = new Complejo(4,-3); System.out.println(numComp.toString()); System.out.println(numComp.Norma()); } } En la clase Complejo tenemos también un ejemplo de un llamado a un método de clase, o sea static: return (float)Math.sqrt(x*x+y*y); Como el método es de clase, no hace falta llamarlo para un objeto en particular. En ese caso, en lugar del nombre de un objeto existente se puede utilizar directamente el nombre de la clase: Nombre_de_la_Clase<punto>Nombre_del_Método(parámetros) En el próximo capítulo veremos las estructuras de control y empezaremos a armar ejemplos para repasar todo esto. 9 ESTRUCTURAS DE CONTROL SENTENCIAS CONDICIONALES Ejecutan un código u otro en función de que se cumplan o no una determinada condición. Pasemos a ver sus principales tipos. 9.1 if…[else] www.javarevolutions.com Canal YouTube: Java Revolutions La más común de todas, permite ejecutar una instrucción (o secuencia de instrucciones) si se da una condición dada (o, mediante la cláusula else, ejecutar otra secuencia en caso contrario). if (expresión_booleana) instrucción_si_true; [else instrucción_si_false;] o bien: if (expresión_booleana) { instrucciones_si_true; } else { instrucciones_si_false; } Por ejemplo: public final String toString() { if (y<0) return x+"-i"+(-y); else return +x+"+i"+y; } 9.2 Switch…case…brake…default Permite ejecutar una serie de operaciones para el caso de que una variable tenga un valor entero dado. La ejecución saltea todos los case hasta que encuentra uno con el valor de la variable, y ejecuta desde allí hasta el final del case o hasta que encuentre un break, en cuyo caso salta al final del case. El default permite poner una serie de instrucciones que se ejecutan en caso de que la igualdad no se de para ninguno de los case. switch (expresión_entera) { case (valor1): instrucciones_1; www.javarevolutions.com Canal YouTube: Java Revolutions [break;] case (valor2): instrucciones_2; [break;] ….. case (valorN): instrucciones_N; [break;] default: instrucciones_por_defecto; } Por ejemplo: switch (mes) { case (2): if (bisiesto()) dias=29; else dias=31; break; case (4): case (6): case (9): case (11): dias = 30; break; default: dias = 31; } BUCLES Son instrucciones que nos permiten repetir un bloque de código mientras se cumpla una determinada condición. Pasemos a ver sus tipos. 9.3 While Permite ejecutar un grupo de instrucciones mientras se cumpla una condición dada: www.javarevolutions.com Canal YouTube: Java Revolutions while (expresión_booleana) { instrucciones… } Por ejemplo: while ( linea != null) { linea = archivo.LeerLinea(); System.out.println(linea); } 9.4 Do…while Similar al anterior, sólo que la condición se evalúa al final del ciclo y no al principio: do { instrucciones… } while (expresión_booleana); Por ejemplo: do { linea = archivo.LeerLinea(); if (linea != null) System.out.println(linea); } while (linea != null); 9.5 For También para ejecutar en forma repetida una serie de instrucciones; es un poco más complejo: for ( instrucciones_iniciales; condición_booleana; instruccion_repetitiva_x ) { instrucciones… www.javarevolutions.com Canal YouTube: Java Revolutions } Si bien las instrucciones pueden ser cualquiera (el bucle se repite mientras la condición sea verdadera), lo usual es utilizarlo para "contar" la cantidad de veces que se repiten las instrucciones; se podría indicar así: for ( contador = valor_inicial; contador < valor_final; contador+ + ) { instrucciones… } Por ejemplo: for ( i=0; i<10; i++ ) { System.out.println( i ); } o, para contar hacia atrás: for ( i=10; I>0; I-- ) { System.out.println( i ); } 9.6 Break y continue Estas instrucciones permiten saltar al final de una ejecución repetitiva (break) o al principio de la misma (continue). Por ejemplo, en: www.javarevolutions.com Canal YouTube: Java Revolutions import java.io.*; class Bucles { public static void main (String argv[ ]) { int i=0; for (i=1; i<5; i++) { System.out.println("antes "+i); if (i==2) continue; if (i==3) break; System.out.println("después "+i); } } } La salida es: antes 1 después 1 antes 2 antes 3 Por qué? "i" comienza en 1 (imprime "antes" y "después"); cuando pasa a 2, el continue salta al principio del bucle (no imprime el "después"). Finalmente, cuando "i" vale 3, el break da por terminado el bucle for. 10 CLASES Y HERENCIA Herencia Cuando en Java indicamos que una clase “extends” otra clase estamos indicando que es una clase hija de esta y que, por lo tanto, hereda todos sus métodos y variables. Este es un poderoso mecanismo para la reusabilidad del código. Podemos heredar de una clase, por lo cual partimos de su estructura de variables y métodos, y luego añadir lo que necesitemos o modificar lo que no se adapte a nuestros requerimientos. Veamos un ejemplo: www.javarevolutions.com Canal YouTube: Java Revolutions class Animal{ protected int edad; String nombre; public Animal(){ } public Animal(int _edad, String _nombre){ edad = _edad; nombre = _nombre; } public void nace(){ System.out.println("Hola mundo"); } public void getNombre(){ System.out.println(nombre); } public void getNombre(int i){ System.out.println(nombre +" " +edad); } public void getEdad(){ System.out.println(edad); } } //La clase Perro extiende a Animal, heredando sus métodos y //variables public class Perro extends Animal{ Perro(){ edad = 0; nombre ="Tobi"; } Perro(int edad, String nombre){ //Esta sentencia invoca a un constructor de la clase padre. super(edad,nombre); } www.javarevolutions.com Canal YouTube: Java Revolutions //Método estático que recibe un objeto de tipo Perro e //invoca a su método getEdad() static void get1(Perro dog){ //El métod getEdad() no está definido en Perro, lo ha //heredado de Animal. dog.getEdad(); } public static void main (String[] args){ //Creamos un objeto de tipo Perro Perro dog = new Perro(8,"Bambi"); //Invocamos al método estátioco get1 pasándole el objeto // de tipo Perro creado. Perro.get1(dog); } } public class Perro2 extends Animal{ Perro2(){ edad = 0; nombre ="Tobi"; } Perro2(int edad, String nombre){ super(edad,nombre); } static void get1(Perro2 dog){ dog.getEdad(); //Cuando ejecutemos este método en vez de ejecutarse el //código de la clase padre se ejecutará el código de la clase //hija, ya que ésta ha sobreescrito este método. dog.getNombre(11); } //Sobreescribe al método de la clase padre. getNombre(int i){ System.out.println(nombre +" " +i); } public static void main (String[] args){ Perro2 dog = new Perro2(8,"hola"); Perro2.get1(dog); } } www.javarevolutions.com Canal YouTube: Java Revolutions public void 10.1 Creación y referencia a objetos Aunque ya hemos visto como se crea un objeto vamos a formalizarlo un poco. Un objeto en el ordenador es esencialmente un bloque de memoria con espacio para guardar las variables de dicho objeto. Crear el objeto es sinónimo de reservar espacio para sus variables, inicializarlo es darle un valor a estas variables. Para crear un objeto se utiliza el comando new. Veámoslo sobre un ejemplo: class Fecha{ int dia,mes,ano; Fecha(){ dia=1; mes = 1; ano = 1900; } Fecha (int _dia, int _mes, int _ano){ dia= _dia; mes = _mes; ano = _ano; } } ///:~ Fecha hoy; hoy = new Fecha(); Con el primer comando hemos creado un puntero que apunta a una variable tipo fecha, como está sin inicializar apuntara a null. Con el segundo inicializamos el objeto al que apunta hoy, reservando espacio para sus variables. El constructor las inicializará, tomando el objeto hoy el valor 1-1-1900. Fecha un_dia = new Fecha(8,12,1999); Con esta sentencia creamos una variable que se llama un_dia con valor 8-12-1999. Una vez creado un objeto será posible acceder a todas sus variables y métodos públicos, así por ejemplo en el ejemplo anterior un_dia.dia = 8. Si la variable fuese privada solo podrían acceder a ella sus instancias: www.javarevolutions.com Canal YouTube: Java Revolutions class Fecha{ private int dia,mes,ano; Fecha(){ dia=1; mes = 1; ano = 1900; } Fecha (int ndia, nmes, nano){ dia= ndia; mes = nmes; ano = nano; } } De este modo no podríamos acceder a las variables de la clase fecha, para acceder a ella tendríamos que hacerlo mediante métodos que nos devolviesen su valor. A esto se le denomina encapsulación. Esta es la forma correcta de programar OOP: no debemos dejar acceder a las variables de los objetos por otro procedimiento que no sea paso de mensajes entre métodos. 10.2 this Es una variable especial de sólo lectura que proporciona Java. Contiene una referencia al objeto en el que se usa dicha variable. A veces es útil que un objeto pueda referenciarse a si mismo: class Cliente{ public Cliente(String n){ //Llamamos al otro constructor. El empleo de this ha de ser //siempre en la primera línea dentro del constructor. this(n, Cuenta.nuevo_numero()); ..... } public Cliente (String n, int a){ nombre = n; numero_cuenta = a; } ..... } Otro posible uso de this, que ya se ha visto en ejemplos anteriores es diferenciar entre variables www.javarevolutions.com Canal YouTube: Java Revolutions locales de un método o constructor y variables del objeto. En los códigos del cd correspondientes a los ejemplos Perro.java y Perro2.java el constructor de la clases Animal aunque realiza la misma función que los que se recogen en estos apuntos son ligeramente diferentes: public Animal(int edad, String nombre){ //this.edad = varaible del objeto Perro //edad = variable definida sólo dentro del constructor this.edad =edad; this.nombre=nombre; } 10.3 super Del mismo modo que this apunta al objeto actual tenemos otra variable super que apunta a la clase de la cual se deriva nuestra clase class Gato { void hablar(){ System.out.println("Miau"); } }///~ class GatoMagico extends Gato { boolean gentePresente; void hablar(){ if(gentePresente) //Invoca al método sobreescrito de la clase padre super.hablar(); else System.out.println("Hola"); } } Uno de los principales usos de super es, como ya se empleó en un ejemplo, llamar al constructor de la clase padre. www.javarevolutions.com Canal YouTube: Java Revolutions 10.4 Interfaces En Java no está soportada la herencia múltiple, esto es, no está permitido que una misma clase pueda heredar las propiedades de varias clases padres. En principio esto pudiera parecer una propiedad interesante que le daría una mayor potencia al lenguaje de programación, sin embargo los creadores de Java decidieron no implementar la herencia múltiple por considerar que esta añade al código una gran complejidad (lo que hace que muchas veces los programadores que emplean programas que sí la soportan no lleguen a usarla). Sin embargo para no privar a Java de la potencia de la herencia múltiple sus creadores introdujeron un nuevo concepto: el de interface. Una interface es formalmente como una clase, con dos diferencias: sus métodos están vacíos, no hacen nada, y a la hora de definirla en vez de emplear la palabra clave “class” se emplea “inteface”. Veámoslo con un ejemplo: interface Animal{ public int edad = 10; public String nombre = "Bob"; public void nace(); public void getNombre(); void getNombre(int i); } ///:~ Cabe preguntarnos cual es el uso de una interface si sus métodos están vacíos. Bien, cuando una clase implementa una interface lo que estamos haciendo es una promesa de que esa clase va a implementar todos los métodos de la interface en cuestión. Si la clase que implementa la interface no “sobrescribiera” alguno de los métodos de la interface automáticamente esta clase se convertiría en abstracta y no podríamos crear ningún objeto de ella. Para que un método sobrescriba a otro ha de tener el mismo nombre, se le han de pasar los mismos datos, ha de devolver el mismo tipo de dato y ha de tener el mismo modificador que el método al que sobrescribe. Si no tuviera el mismo modificador el compilador nos daría un error y no nos dejaría seguir adelante. Veámoslo con un ejemplo de una clase que implementa la anterior interface: www.javarevolutions.com Canal YouTube: Java Revolutions interface Animal{ public int edad = 10; public String nombre = "Bob"; public void nace(); public void getNombre(); void getNombre(int i); } ///:~ public class Perro3 implements Animal{ Perro3(){ getNombre(); getNombre(8); } //Compruévese como si cambiamos el nombre del método a nac() //no compila ya que no henos sobreescrito todos los métodos //de la interfaz. public void nace(){ System.out.println("hola mundo"); } public void getNombre(){ System.out.println(nombre ); } public void getNombre(int i){ System.out.println(nombre +" " +i); } public static void main (String[] args){ Perro3 dog = new Perro3(); //Compruevese como esta línea da un error al compilar debido //a intentar asignar un valor a una variable final // dog.edad = 8; } } ///:~ Las variables que se definen en una interface llevan todas ellas el atributo final, y es obligatorio darles un valor dentro del cuerpo de la interface. Además no pueden llevar modificadores private ni protected, sólo public. Su función es la de ser una especie de constantes para todos los www.javarevolutions.com Canal YouTube: Java Revolutions objetos que implementen dicha interface. Por último decir que aunque una clase sólo puede heredar propiedades de otra clase puede implementar cuantas interfaces se desee, recuperándose así en buena parte la potencia de la herencia múltiple. interface Animal1{ public int edad = 10; public String nombre = "Bob"; public void nace(); } interface Animal2{ public void getNombre(); } interface Animal3{ void getNombre(int i); } public class Perro4 implements Animal1,Animal2,Animal3{ Perro4(){ getNombre(); getNombre(8); //edad = 10; no podemos cambiar este valor } public void nace(){ System.out.println("hola mundo"); } public void getNombre(){ System.out.println(nombre ); } public void getNombre(int i){ System.out.println(nombre +" " +i); } public static void main (String[] args){ Perro4 dog = new Perro4(); } } www.javarevolutions.com Canal YouTube: Java Revolutions