Ir arriba

J A V A: Sintaxis

La clase describe la estructura del objeto.
Se define con la palabra reservada class,
seguida del nombre de la clase.
Luego, entre llaves, se define su estructura.
Sus datos son atributos.

Variable, dato, función o rutina, se define dentro de una clase.
No existen variables globales o funciones aisladas.
Las funciones y rutinas son sus métodos.
Un objeto es instancia operativa de una clase.

Mi 1er
Applets
Colores
Imagen
Tuti
Fondos
Música
Maestro
Ejemplo:
Definimos una clase, con dos atributos enteros x e y.
Esos atributos se declaran con el modificador public:
Indica que esos datos son accesibles desde otras clases, es decir, son públicos y conocidos por cualquier otro objeto.

Ejemplo:
Define una clase, pero no crea ningún objeto.
Para alguna otra clase o métodos, declarar un objeto de la clase Punto, basta poner.

class Punto {
public long x;
public long y;
}

Punto origen;

Creará una variable que referencia, apunta, a un objeto de la clase Punto.
El objeto aún no está creado; solamente se declara una variable, un atributo, que luego, referenciará un objeto de la clase Punto.
En la creación, el atributo origen no apunta a ningún objeto, y tiene como valor reservado null, es decir, no apunta a nada.
Si ya tenemos un objeto Punto en la variable p, podemos acceder a sus atributos con expresiones como:

int coordenada = p.x;

escribiendo de la forma:
< Objeto >.< Atributo >

No siempre es posible hacer esto, pues no todos los atributos de un objeto pueden estar disponibles para que puedan ser accedidos en cualquier lugar.
No vemos como crear un objeto, pero si lo creamos, las variables miembros son inicializados a 0 o a NULL , según el tipo.
Podemos iniciarlas a algún valor, en su declaración:

classs Persona {
int edad = 21;
int peso = 60;
}

Ejemplo de clase que puede contener atributos que no son tipos primitivos, sino referencias a otros objetos:

class Rectángulo {
Punto origen;
int ancho;
int alto;
}

La clase tiene tres atributos: dos son tipos primitivos (enteros), pero el atributos origen está definido como va a referenciar a un objeto, una instancia de la clase Punto.
Al comienzo este atributo no contendrá ningún objeto: debemos crearlo.

Métodos
Son rutinas y funciones que están asociadas a la clase, que operan sobre sus datos.
Se definen en el interior de la clase.
En nuestro ejemplo:

class Rectángulo {
Punto origen;
int ancho;
int alto;

int tomarArea () {
return ancho*alto;
}

void ponerAncho (int nuevoAncho) {
ancho = nuevoAncho;
}

void ponerAlto (int nuevoAlto) {
alto = nuevoAlto;
}

void mover (Punto nuevoOrigen) {
origen = nuevoOrigen;
}
}

Se agregó algunos métodos.
Primero, TomarArea, es una función que nos devuelve el área del rectángulo.
Al declararlo:

int tomarArea () {

Pusimos un tipo (int, devuelve entero) de resultado.
Luego viene el nombre (tomarArea), y los parámetros que recibe, entre paréntesis (en este caso ninguno).
Al abrir la llave, comienza el bloque de sentencia que compone el método.
Este método devuelve algo, usando el verbo return, seguido de una expresión resultado:

return ancho*alto;

El tipo de la expresión se convierte al tipo declarado como el resultado a devolver (en este caso, int).
El próximo método ya no es una función, pues no devuelve nada. Al declararlo:

void ponerAncho (int nuevoAncho) {

Su tipo es void (sin valor de retorno).
Este método espera un argumento, de tipo entero. Vemos que ese parámetro será el nuevo valor de la variable miembro ancho.
Si tenemos creado y funcionando, un objeto de la clase Rectángulo, y lo llamamos r.
Podemos operar sobre él de esta forma:

r.ponerAncho (10);
r.ponerAlto (5);

int area = r.tomarArea ();

Observemos que para invocar a un método de un objeto, escribimos la forma:

< Objeto >. < Método > ( { < parámetros > } )

(Las llaves { } indican que los parámetros son opcionales).
Es decir, primero nombramos al objeto, y luego de un punto, ponemos el método que invocamos, siempre seguido de paréntesis, que pueden contener los argumentos que necesita.
Un error muy frecuente es poner:

r.tomarArea;

Es incorrecto, y el compilador lo rechazará: faltan los paréntesis. Sí está permitido:

int ancho = r.ancho;

Así es como se accede a una variable miembro inmediatamente.

"Referencias a un mismo objeto"

En el último método definido:

void mover (Punto nuevoOrigen) {

definimos que recibe un parámetro, que resulta ser un objeto (en rigor de verdad, es una referencia a un objeto).
Ese argumento es de la clase Punto, y termina siendo asignado a la variable miembro origen:

origen = nuevoOrigen;

Esta asignación no copia el contenido de un objeto a otro, sino que hace que dos variables, dos referencias, apunten al mismo objeto.

Si tenemos dos rectángulo r1 y r2, cada uno ya creado e inicializado, cuando hacemos:

r1.origen = r2.origen;

de ahora en más, ambos rectángulos tienen el mismo objeto Punto como punto de origen del rectángulo. Si ejecutamos:

r1.origen .x = 0;

cambiando la coordenada x del origen de r1, cambiamos también el valor del origen de r2.
Vistas estas cuestiones podríamos definir mejor el método del problema como:

Void mover (Punto nuevoOrigen) {
origen.x = nuevoOrigen.x;
origen.y = nuevoOrigen.y;
}

Si podemos acceder a las variables miembros de la clase Punto.

"El objeto actual"
Cuando escribimos:

return x*y;

para calcular el área, no tuvimos necesidad de indicar a qué objeto pertenecían x e y.
Cuando no se pone el objeto antes de atributo, se asume que la variable es un miembro del objeto, o es una variable local o parámetro.
Si los nombres colisionan, como podría ser en el siguiente método de Punto:

Void mover (int x, int y) {
this.x = x;
this.y = y;
}

usamos this para indicar al objeto actual.
Esta palabra reservada siempre está apuntando al objeto actual, y puede usarse como arriba, para resolver una ambigüedad, o puede usarse,
por ejemplo, como parámetro de un método de otro objeto:

objeto.procesar(this);

"Variables locales a los métodos"
Cuando declaramos variables miembro, estas variables están "dentro" del objeto y viven con él.
En un método, aparte de las variables propias del objeto y los parámetros que recibe, podemos declarar, en cualquier momento, variables locales:

double binomio (double a, double b)
{
double aa;

aa = a * a;

double ab;

ab = a * b;

double bb;

bb = b * b;

return (aa+ab+bb) ;
{

Las variables que declaramos se pueden iniciar con algún valor en el mismo momento de declararlas:

double ab = a*b;

"Métodos Sobrecargados"
Examinemos el siguientes código:

class Punto {
int x;
int y;

void mover (Punto nuevo) {
x = nuevo .x;
y = nuevo .y;
}

void mover(int x, int y) {
this .x = x;
this .y = y;
}

En Java podemos definir dos métodos con el mismo nombre, siempre que tengan diferentes tipos de argumentos.
En el primer método mover, pasamos como objeto todo un Punto, mientras que en el segundo método, pasamos dos coordenadas enteras.
El compilador sabe a cuál de los dos métodos invocar, según el tipo de argumentos con que se lo invoque.

"Constructores"
Cuando creamos un objeto, por ejemplo, de la clase Punto, sus variables miembros toman los valores de sus inicializadotes (o 0 o null, si no pusimos ningún valor inicial en su declaración).
Hay otra manera de poner valores iniciales, y es con métodos llamados constructores.
Un método constructor no tiene tipo, y lleva el mismo nombre que la clase:

class Punto {
int x;
int y;

Punto (int x, int y) {
this.x = x;
this.y = y;
}

Punto (Punto p) {
x = p.x;
y = p.y;
}
}

Dosos constructores (notamos que se pueden sobrecargar).
Al primero se le pasan dos argumentos, las coordenadas iniciales.
En el segundo, se pasa un punto completo, que debe servir de referencia para los valores iniciales.
Si no lo declaráramos, existe un constructor asumido:

Punto () {
x = 0;
y = 0;
}

"Crear de objetos"
La creación de un objeto: en este caso, un punto, con valores iniciales pasados a su constructor:

Punto p = new Punto (1,2);
Declaramos una variable local, que apuntará a objetos Punto, y que inmediatamente la iniciamos con un nuevo objeto de esa clase.
La clave de la operación es el operador new, y el constructor.
Mientras que un constructor se ocupará de poner en el estado inicial a un objeto recién creado, el operador new es realmente el mago que lo crea.
Si se crea un objeto, en algún momento de la operación llegaremos a un new.
Otras opciones, como en:

Punto p1;
p1 = new Punto ();

Punto p2;
p2 = new Punto (p1);

Punto p3;
p3 = p2;

La última asignación no crea ningún objeto, solamente hace que p2 y p3 apunten y referencia al mismo objeto (recordemos siempre que una declaración, a secas, no crea ningún objeto).

"Muerte del objeto"
- En algún momento de la ejecución del programa, nacen, al pasar recién por un constructor, con sus miembros frescos, y sus métodos listos para el trabajo.
Luego con el paso del tiempo, el objeto, tan útil, puede ser descartado, y no ser necesario más en la lógica del programa.

- Si el programador puede crear objetos, su destrucción, la destrucción total, queda en manos de la máquina virtual de Java.

- Cuando introdujimos los objetos, inmediatamente tuvimos que referenciarlos en variables, que pueden ser miembros de alguna clase, o parámetros, o variables locales.
También notamos que dos variables pueden referenciar al mismo objeto.
El entorno Java se encarga de asignar espacio a los objetos que creamos con new.
Y ese espacio se libera, y el objeto se destruye completamente, cuando el entorno Java, que siempre vigila, detecta que ese objeto NO ES MAS REFERENCIADO, por ningún objeto activo del sistema.

- Esto lo consigue, porque paralelamente a nuestro programa, la maquina virtual Java va ejecutando un proceso denominado "garbage collector" (recolector de basura), que detecta esos objetos desreferenciados completamente, y los destruye, liberando su espacio.

- Esto tiene consecuencias notables para nosotros, programadores. Ya no tenemos que preocuparnos más de decidir cuando un objeto debe ser liberado o no. En C++ las referencias a objetos dinámicos se basan en punteros.

- Uno podría destruir un objeto referenciado por un puntero pero, ¿qué pasa si hay otro puntero que referencia a la misma dirección/objeto?
Conseguimos que ese segundo puntero esté referenciando a algo inválido, a memoria liberada.
Dos errores muy frecuentes (frecuentísimos) en los lenguajes como C++ son destruir objetos que no deben ser destruidos y olvidarse de destruir objetos que no ya no necesitamos.

En Java, esos problemas no los encontramos.
Ejemplo:

Punto p1 = new Punto (1,2);
Punto p2 = p1;
p1 = null;

El objeto creado con el new, queda referenciado por p1.
Luego p2 queda apuntando al mismo objeto.
Hacemos que p1 apunte a nada.
Al final de esta secuencia, el objeto sigue existiendo, y tiene una referencia, la de p2.
Cuando hagamos:

p2 = null;
O
p2 = new Punto ();

Al hacer que p2 apunte a otro objeto, el objeto inicial quedara sin referencias que lo apunten, y no podrá ser más accedido por ningún elemento del programa.
De ahí en mas, el objeto puede ser destruido en cualquier momento (no necesariamente es destruido de inmediato, sino cuando el entorno Java y su "garbage collector" lo decidan).

Cuando cualquier objeto es destruido, se invoca su método:

proctected void finalize() throws Throwable { .

que podemos redefinir para realizar las últimas tareas antes de su desaparición.

"La referencia null"

Podemos tener el valor null.
Este valor indica que no estamos apuntando a ningún objeto.
Es el valor que toman los miembros de un objeto que referencian a otros objetos, en el momento de su creación, cuando no le damos otros valores.
El valor null puede ser devuelto por ciertos métodos, que devuelven referencias a objetos, indicando, por ejemplo, que no existe el objeto resultado pedido.
Es inválido invocar cualquier método de una referencia, si esta contiene null.
Podemos preguntar si la referencia tiene o no null.

if (referencia != null)
System.out.println("Existe referencia");

"Operadores sobre referencia"
Comparábamos si una referencia era distinta de null.
Los operadores que podemos usar, para comparar referencias, son el == (igual), el != (distinto) y el instanceof.
Los dos primero sólo necesitan una aclaración: dos referencias son iguales, si y sólo si apuntan al mismo objeto.
La igualdad, en ese caso, entre dos rectángulo, no se basa en los valores de sus variables, sino en los valores de sus referencias.
Si queremos comparar valores ( por ejemplo, si dos rectángulo tienen el mismo punto de origen, el mismo ancho y el mismo alto), debemos recurrir a un método propio de comparación.
El instanceof se usa así:

if (r instanceof Rectángulo)
Determina si un objeto es instancia, pertenece a una clase.
Veremos mas adelante, que r puede pertenecer a una subclase de Rectángulo.
Aun así, el resultado de r instanceof Rectángulo sería verdadero.

"Herencia"
En la programación orientada a objetos, uno de los conceptos clave es el de la herencia. Podemos entenderla así: cuando definimos una clase cualquiera, establecemos sus atributos y métodos. Puede ocurrir que necesitemos crear otra clase, que resulta un caso especial de la anterior. Podríamos tener la clase Empresa ya definida, y necesitar ahora definir la clase Cliente. Nos damos cuenta de que cualquier atributo o método que tenga Empresa (como razón social, domicilio, teléfono) también lo necesita Cliente. En este caso, podemos construir a Cliente de tal forma que sea un caso especial de Empresa, quizá con atributo y métodos adicionales, como condición de venta y consulta de saldo. Decimos entonces que Cliente hereda todos los atributos y métodos de Empresa.
Además de agregar métodos, podemos redefinir métodos de la clase inicial. Supongamos que tenemos una clase Cuadrilátero, con un método área que calcula su área. Ahora creamos una clase Rectángulo que hereda de Cuadrilátero. Podemos redefinir su método área, para que lo calcule de otra forma.
Pero es interesante destacar que no es necesario redefinir todos los métodos. Si un método no se redefine, se hereda sin alterar el método de la clase inicial.

"Subclase"
¿Cómo se implementa la herencia en Java? Al declarar una clase, podemos indicar que es una clase derivada de otra, de la cual hereda todos sus atributos y métodos. Se declara, por ejemplo:

class button extends Component {

Esta es un declaración de la clase Button (botón), como subclase de Component (componente de interfase de usuario).
La palabra clave es extends (extiende).
En java, se entiende que una subclase "extiende" las capacidades y conductas de la clase madre.

"Redefinición de métodos y super"
Sean dos clases:

class Empresa {
....
// Método de Impresión de datos de una empresa
void imprime() {
....
}
}

class Cliente extends Empresa {
...
// Método de impresión de datos de un cliente
void imprime() {
super.imprime(); // Datos de Empresa
// Otras instrucciones de impresión
}
}

Una clase Cliente es subclase de la otra, Empresa.
Las dos tiene definido un método imprime, que posiblemente imprime sus datos.
Cuando definimos el método de impresión de Cliente, queremos aprovechar el método ya definido de Empresa. ¿Cómo lo invocamos?
Con la expresión super.imprime( ).
La seudo-variable super apunta al mismo objeto que this pero con métodos de su superclase inmediata.
En este caso, this.imprime( ) es el método de Cliente, mientras que super.imprime( ) es el método de impresión de Empresa.
Podemos redefinir un método sin necesidad de invocar a su supermétodo, y hasta podemos invocarlo en cualquier momento (al principio, en el medio, al final del nuevo método).

"Clases, atributos y métodos finales"
En algún momento, puede querer crear una clase, que no pueda extenderse en ninguna subclase, quizás por razones de seguridad.
Puede definirla como:

class final class Contraseña {

La palabra clave final indica que esta clase no puede extenderse.
Otras veces, no queremos que se pueda redefinir un método. Podemos escribir:

final void ingresaContraseña() {

Cuando queremos que un atributo permanezca constante, podemos ponerle un valor inicial, y con la palabra clave final lo marcamos como inalterable:

final int ncolumnas = 8;

Es la forma de declarar constantes.
Hemos utilizado algunas, como Font.PLAIN, o Color.yellow.
Para los programadores C/C++, es lo más cercano a un #define de una constante.

"Atributos y métodos de clase"
Los métodos como el main que han sido calificados con la palabra reservada static.
Un método o atributo estático es uno que no está dentro de cada objeto de la clase, sino que existe un único representante para toda la clase en la que está definido.
El ejemplo clásico de un atributo estático es un contador de objetos creados de una clase.

class Empleado {
static int empleados = 0;

Empleado() {
empleados++;
}
....
}

Un método estático no se invoca con objeto, pues no lo necesita; puede invocarse antes de la creación de cualquier objeto de su clase.
Hay ejemplo estáticos definidos en paquetes y clases, para uso del programador, como por ejemplo el conversor de String a enteros:

int i = Integer.parseString("123");
"Lo público, lo privado, lo protegido"

Hemos definido clases, sus atributos y métodos.
La palabra clave public, que hemos usado para definir una clase pública en cada archivo .java que hemos construido.
Cuando definimos una clase, un método o un atributo podemos calificarlo como public, private o protected.
Cuando declaramos un método o atributo de una clase como público, establecemos que puede ser accedido desde otros puntos del sistema.
Cuando creamos un objeto de una clase, todos los atributos y métodos definidos como públicos pueden ser usados e invocados, en métodos de la misma clase, o de otras clases.
En tecnología de objetos se trata de no declarar todo público, en especial los atributos.
Con esto se logra el llamado encapsulamiento, donde las variables de un objeto quedan ocultas, para impedir que puedan ser modificadas por cualquiera.
Lo que se trata de publicar son los métodos, la conducta del objeto, y no su implementación.
Para conseguir que un atributo o método sólo pueda usarse dentro de la clase en la que esta definido, se puede calificar como private:

private void verificaContraseña() {
Al declarar como privado un miembro de una clase, sólo puede ser invocado y usado en métodos de esa clase. Ni siquiera esta disponible para las subclases.
Si queremos relajar un poco esta restricción, podemos usar el calificador protected. En este caso, el método o atributo marcado como protegido, puede ser accedido en las subclases que se definan, de la clase inicial, pero seguirá siendo inaccesible a los demás objetos.
Las clases no pueden marcarse como private o como protected.
Cuando a un atributo o método no lo calificamos con nada, entonces el compilador asume que puede ser accedido por las demás clases, pero sólo del mismo paquete, que es un conjunto de clases. Ya veremos paquetes, como el java.awt, que agrupan clases de interfase de usuario. "Ejemplo:"

Personas1.java

//Ejemplo de clases, objetos, constructores y polimorfismo

class Persona {
string nombre;
public Persona() [ System.out.println();
System.out.println("Se crea una persona");
}
public Persona(String n) {
this();
System.out.println("Soy "+n);
nombre = n;
}
public void quienSoy() {
System.out.println();
if (nombre != null)
System.out.println("Mi nombre es "+nombre);
System.out.println("Soy una persona");
}
}

class Mujer extends Persona {
public Mujer(String n) {
super(n);
}
public void quienSoy() {
super.quiensoy();
System.out.println("Soy una mujer");
}

class Hombre extends Persona {
public Hombre(String n) {
super(n);
}
public void quienSoy() {
super.quienSoy();
System.out.println("Soy una hombre");
}

public class Personas {
static public void main(String [ ] args) {
Persona persona1 = new Persona();
Persona persona2 = (Persona) new Mujer("Claudia");
Persona persona3 = (Persona) new Hombre ("Gustavo");
persona1.quienSoy();
persona2.quienSoy();
persona3.quienSoy();
}
}

Definimos las clases Persona, Mujer y Hombre, en una jerarquia.
Luego creamos objetos y jugamos con el polimorfismo.

En este ejemplo creamos tres clases: una principal (Persona) y dos subclases suyas:Mujer y Hombre.
Usamos dos constructores: uno sin parámetros y otro con un parámetro (el nombre de la persona a construir).
En los constructores hemos puesto mensajes en pantalla, para hacer visible el proceso de creación de los objetos.
Observar que en el constructor con parámetro de Persona, invocamos al constructor sin parámetros.

public Persona(string n) {
this();

Definimos un método, común a las tres clases, llamado quienSoy, que pone en pantalla el nombre de la persona, y, en el caso de Hombre y Mujer, su género.
En el método de las subclases aprovechamos y llamamos al "supermétodo", ya efinido en Persona:

public void quiensoy() {
super.quiensoy();

En la rutina main (que ahora sabemos porqué es estática), creamos tres objetos, uno de cada clase, pero que son referenciados por tres variables de la clase Persona.

Persona persona1 = new Persona();
Persona persona2 = (Persona) new Mujer("Claudia");
Persona persona3 = (Persona) new Hombre("Gustavo");

Observamos el uso de (Persona). Esta es una expresión para tomar un referencia, y tratarla como una referencia a Persona. Sin embargo, el objeto al que sigue apuntando no cambia.
Lo interesante de esto viene en las invocaciones de los métodos:

persona1.quienSoy();
persona2.quienSoy();
persona3.quienSoy();

Aunque el compilador sólo sabe que persona1, persona2, persona3, son referencias a Persona, en el momento de ejecución, cada uno apunta a un objeto de una clase distinta. Cuando la maquina virtual Java invoca al método quienSoy del objeto persona3, llega a descubrir que esa referencia apunta a un objeto de la clase Hombre, y termina invocando al quienSoy definido en esa clase.
Todo esto se puede apreciar mejor, en la salida del programa (ver figura 8.4)

Salida de la aplicación Persona

Se crea un persona

Se crea un persona
Soy Claudia

Se crea una persona
Soy Gustavo

Soy una persona

Mi nombre es Claudia
Soy una persona
Soy mujer

Mi nombre es Gustavo
Soy una persona
Soy hombre

Ejemplo que ilustra esa capacidad de Java, de tratar a un objeto de acuerdo con su clase en ejecución, en lugar de lo declarado a nivel de texto de código fuente. Interpretó correctamente a persona3 como un objeto Hombre, a pesar de estar declarado como Persona. Esta conducta (creada a propósito en la definición del lenguaje) se denomina polimorfismo (varia formas, conductas para un mismo método).

"Interfases"
En el lenguaje Java, además de clases, existe el concepto de interfase, que es importante a la hora de entender ciertas clases del lenguaje.
Una interfase consiste en una declaración de métodos, sin su implementación, sin su código.
Entonces, ¿Para qué sirve? Sirve de molde, podríamos decir, para que a una clase la declaremos como que soporta esa interfase.
Podemos definir una interfase que afirme la existencia de dos métodos,leer y grabar.
La podemos definir como:

interface Archivable {
void leer();
void grabar();
}

De ahí en más, cuando queramos afirmar que una clase posee métodos para leerse y grabarse, podemos escribir:

class Documento implements Archivable {
....
}
class Cliente extends Empresa implements Archivable {
....
}

Esto no nos libra de escribir las implementaciones de esos métodos en Documento y en Cliente. Pero es una forma de agrupar métodos y usarlo, definirlos, en clases distintas, que no tienen por qué estar en la misma jerarquía ni estar relacionadas. Para los que conocen algo de programación orientada a objetos, ésta es la forma que tiene Java de acercarse algo a la herencia de múltiple clases.

"Paquetes"
Otros lenguajes orientados a objetos sólo se preocupan de definir clases. Como las clases pueden crearlas distintos programadores, y hasta distintas empresas,
puede ocurrir que en una librería de manejo de base de datos, exista una clase Tabla,
mientras que en otra librería, de clases matemáticas, también puede haber una clase Tabla.
Java aborda el problema, definiendo las clases que brinda, no como una gran conjunto, sino como clases agrupadas en paquetes.
Cada paquete tiene un nombre, que puede consistir de palabras unidas por puntos, como java.io, java.applet.
Incluso podemos crear nuestros propios paquetes como com.mp.libros.
Cuando uno accede a una clase, y esta en un paquete, debemos poner el nombre del paquete.
En general nos libramos de esto, pues en nuestros programas importamos el nombre del paquete, en declaraciones como:

import java.applet.* ;

En caso de tener que indicar que la clase Applet pertenece al paquete java.applet, deberíamos escribir expresiones como:

class MiApplet extends java.applet.Applet {

Si quisiéramos que la clases definidas en un archivo .java, fuera a incluirse en un paquete, podemos declarar:

package com.mp.libros;

Al principio del texto del archivo .java, para indicarle al compilador, que las clases que vaya a generar, pertenecerán a ese paquete.