Este es el segundo tutorial de Java. En el se explica en detalle dos conceptos básicos de la OOP (Programación Orientada a Objetos). En nuestro primer tutorial de Java Hola Mundo! en Java vimos que para que exista un objeto (algo concreto) siempre debe existir una clase que es donde se definen las características que tendran todos los objetos (las instancias) de esa clase. Ahora, en este artículo, trataré de explicar mejor que es una clase, que son objetos y veremos cómo se compone una clase (en terminología OOP, hablaremos de los miembros de una clase). En el mundo real, nos encontramos a menudo con muchos objetos individuales, todos del mismo tipo. Por ejemplo, puede haber miles de automóviles en existencia, todos de la misma marca y modelo (supongamos Toyota Yaris). Cada automóvil de ese tipo, se construye a partir del mismo conjunto de planos y por lo tanto, contiene los mismos componentes. En términos de orientación a objetos, se dice que un automóvil Toyota Yaris en particular es una instancia de la clase de objetos conocidos como automóviles Toyota Yaris.
Nota: Una clase es el plano del cual se crean los objetos individuales (las instancias).
Así para poder trabajar con objetos que representen automóviles Toyota Yaris en nuestro programa, tenemos que crear primero la clase (o sea el plano donde estableceremos todas las características que tienen esos automóviles -los miembros de la clase). El nombre que le daremos a nuestra clase, debe seguir las reglas de los identificadores de Java (es decir, no puede empezar con un número, no puede tener caracteres especiales, excepto "_", ni puede contener espacios en blancos.) Un nombre correcto para nuestra clase podría ser ToyotaYarisCar.
Nota: Observe como escribí el nombre de la clase. Esto es una convención (se llama notación Pascal) y siempre la usaremos al ponerle nombre a las clases. La notación Pascal escribe el identificador con la primera letra en mayúsculas y en caso (como en el nuestro) que esté formado por varias palabras, cada palabra también se inicia en mayúsculas. Existe otra notación llamada notación Camel que consiste en escribir la primera palabra en minúsculas y las siguiente en mayúsculas. Esta notación la usaremos para los parámetros de los métodos. Por ejemplo, los siguientes son buenos identificadores que siguen esta notación: imprimeNumeros, edad, nombreAutor, etc.
Para hacer un ejemplo de lo que estamos explicando usaremos NetBeans. Creamos un nuevo proyecto del tipo Java Application, lo nombramos como Ej_Clases y dejamos todas las opciones por defecto. NetBeans, nos genera el proyecto y nos presenta en el editor la clase principal del programa en donde se encuentra el método main (que como vimos en el artículoHola mundo en Java, es el punto de entrada del programa).
package ej_clases;
public class Ej_Clases {
public static void main(String[] args) {
// TODO code application logic here
}
}
Ahora es el momento para agregar la clase ToyotaYarisCar a nuestro programa. Para esto, seguimos los siguientes pasos:
-
-
Vamos al menu File, y seleccionamos New File.
-
En la ventana que emerge, seleccionamos a la izquierda (en categorías) Java, y a la derecha (File Types), Java Class.
-
Hacemos click en Next.
-
En la siguiente ventana, sólo cambiamos el nombre de la clase por el elegido (en nuestro caso ToyotaYarisCar).
-
Hacemos click en Finalize y NetBeans, generará la nueva clase y cargará el archivo generado, en el editor, para que vayamos completando las características de la nueva clase.
package ej_clases;
public class ToyotaYarisCar {
}
Nota: Observe que NetBeans agregó en la declaracíon de la clase ToyotaYarisCar, la palabra reservada public. Esto es lo que se llama el nivel de acceso, o sea desde dónde será visible nuestra clase.
-
Una clase sólo puede utilizar 2 niveles de acceso:
-
público: una clase con este nivel de acceso será visible por cualquier componente que quiera interactuar con ella
-
por defecto: una clase con este nivel de acceso será visible solamente en el mismo package. No tiene palabra reservada para definirlo. Simplemente lo damos cuando en la definicion de la clase no escribimos public (en nuestro caso, si quisiéramos darle este nivel de acceso a nuestra clase ToyotaYarisClass, escribiriamos:
package ej_clases;
class ToyotaYarisCar {
}
Miembros de una clase
Los miembros de una clase son sus componentes. Una clase puede tener los siguientes miembros:
-
Campos (las variables internas)
-
Métodos (las acciones o reponsabilidades)
Mientras (como vimos), una clase solamente puede utilizar 2 niveles de acceso (público y por defecto), los métodos y variables pueden usar los siguientes 4:
-
público (modificador public)
-
por defecto (cuando se omite el modificador)
-
protegido (modificador protected)
-
privado (modificador private)
Al hablar de que la clase A tiene acceso a un miembro de la clase B (independientemente si es una variable o método) decimos implícitamente que el miembro de B al que se está accesando es 'visible' para A. Cuando se trata de acceder a un miembro que no es visible, el compilador mandará un error y no se podrá ejecutar la aplicación. Para poder entender el comportamiento y acceso de ciertos miembros de una clase debemos entender 2 cosas:
-
Cuándo una clase puede acceder a un miembro de otra.
-
Cuándo una clase puede heredar a otra algún miembro.
Lo primero se refiere a cuando una clase puede acceder al miembro de otra clase por medio del operador punto (.), ya sea para invocar un método o retribuir el valor de una variable. P. ej.:
package paqueteUno;
public class ControlAcceso {
public static void main(String[] args){
MiembrosDeClase mdc = new MiembrosDeClase();
System.out.println("Default: "+ mdc.enteroPorDefecto);
System.out.println("Protected: "+ mdc.enteroProtegido);
System.out.println("Default: "+ mdc.enteroPublico);
System.out.println("Private: "+ mdc.enteroPrivado); //1
}
}
class MiembrosDeClase{
public int enteroPublico = 1;
protected int enteroProtegido = 2;
private int enteroPrivado = 3;
int enteroPorDefecto = 4;
}
Si compilamos el código anterior, al llegar a la línea comentada con 1, el compilador mandará un error debido a que se quiere acceder a una variable privada desde otra clase...
ControlAcceso.java:26: enteroPrivado has private access in
paqueteUno.MiembrosDeClase
System.out.println("Private: "+ mdc.enteroPrivado);
1 error
Si quitamos el código que manda a llamar la variable enteroPrivado no aparecerán errores al momento de compilar y se imprimirán los valores de las variables sin problemas:
Default: 4
Protected: 2
Default: 1
En caso de querer ver el valor de las variables desde una clase fuera del paquete paqueteUno, solo podremos acceder a aquellas marcadas como públicas y protegidas, ya que el control de acceso por defecto solamente proporciona visibilidad a aquellas clases dentro del mismo paquete y los miembros privados solamente pueden ser accedidos desde la misma clase que los define.
En resumen , estos son los 4 modificadores de acceso para los miembros de un a clase:
-
Acceso público (modificador public): Cualquier clase dentro del universo de Java podrá tener acceso a un miembro público.
-
Acceso por defecto (cuando se omite algún modificador): Solamente se podrá acceder a dicho miembro si se pertenece al mismo paquete.
-
Acceso protegido (modificador protected): Solamente se podrá acceder a un miembro protegido cuando se está en el mismo paquete o a través de la herencia.
-
Acceso privado (modificador private): Solo se puede acceder dentro de la misma clase.
Acceso a variables locales:
Las variables locales son aquellas que están declaradas dentro de un método, las variables de instancia son aquellas dentro de la clase pero fuera de los métodos. P.ej.:
public class Variables{
int variableDeInstancia = 1;
public void verVariables(){
int variableLocal = 0;
}
}
Es importante tomar en cuenta que las variables de instancia pueden controlar su acceso con los modificadores que hemos visto anteriormente, sin embargo, las variables locales no, al intentar hacerlo el compilador mandará un error. P.ej.:
public class Variables{
private int variableDeInstancia = 1; //no hay problema
public void verVariables(){
protected int variableLocal = 0; //error!
}
}
El único modificador aplicable a una variable local es final.
Métodos y variables finales:
Anteriormente hemos mencionado cómo es que se comporta una clase marcada con el modificador final, ahora es el turno de los miembros de una clase. Al marcar un método con el identificador final estamos indicando (al igual que en las clases) que dicho método no puede ser sobreescrito, sino que debe de quedarse tal cual se definió originalmente. P.ej.:
class SuperClase{
public final void metodoFinal(){
//hacer algo
}
}
class SubClase extends SuperClase{
public void metodoFinal(){
//hacer otra cosa
}
}
Al tratar de compilar el código anterior, aparecerá un error que indica que no se puede sobreescribir el método final...
FinalTest.java:5: The method void metodoFinal() declared in class
SubClase cannot override the final method of the same signature
declared in class SuperClase.
Final methods cannot be overridden.
public void metodoFinal() { }
1 error
Así como los métodos, las variables pueden utilizar el modificador final, en su caso, al declarar una variable final significa que no se puede cambiar su valor una vez que fue asignado. P. ej.:
class EjemploVariableFinal{
final int f = 4;
public void cambiarValor(){
f +=2; //error!
}
}
En el código anterior, se define y asigna un valor a la variable final f, posteriormente el método cambiarValor() intenta incrementar el valor de f, al momento de llegar a esta línea, el compilador arrojará un error indicando que no es posible reasignar un valor a una variable final. Es importante mencionar que cuando se trata de una variable de instancia final(dentro de la clase pero fuera de un método), antes de compilar, se debe de definir su valor, de lo contrario, aparecerá un mensaje de error indicando que no ha sido inicializada, no así con las variables locales.
Métodos abstractos:
Un método abstracto es aquel que se define dentro de una clase pero no se especifica su comportamiento, en lugar de utilizar llaves para definir el código de su comportamiento de finaliza su declaración con un punto y coma (;). Se definen métodos abstractos cuando se quiere forzar a todas las clases que heredan de la clase que tiene dicho método a implementar cierto comportamiento. P. ej.:
public abstract void metodoAbstracto();
Cuando se define al menos un método abstracto dentro de una clase, la clase debe a su vez ser abstracta, de lo contrario el compilador arrojará un error. Asimismo, una clase abstracta podrá no tener ningún método abstracto. Una clase que herede a otra clase abstracta, debe de implementar todos los métodos abstractos de la clase heredada, a menos de que la clase que está heredando sea a su vez abstracta. La regla es la siguiente:
"La primera clase no abstracta que herede a una clase abstracta deberá definir todos los métodos abstractos que le han sido heredados". En caso de que una clase no abstracta no implemente los métodos abstractos definidos en su superclase habrá un error de compilación.
Por último, nunca se debe utilizar el modificador abstract con el modificador static...
abstract static void metodo(); //generará un error de compilación