1 Declaracion de Clases e Interfaces

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 31

Preparación para la certificación

DECLARACIÓN DE CLASES E
INTERFACES
SEMANA 7

[ HERRAMIENTAS PARA LA
DECLARACIÓN DE CLASES E INTERFACES
Programación orientada a objetos, Clases y Objetos

En programación orientada a objetos, los sistemas pueden describirse como una comunidad
de agentes que interactúan entre si por medio de mensajes, asumiendo responsabilidades y
respondiendo a requerimientos de otros agentes del sistema. Cada agente, dentro de un
diseño orientado a objetos, será reconocido como un objeto, y cada objeto tiene un papel
determinado gracias a que goza de un comportamiento y un estado. Es fácil reconocer los
comportamientos que pueden presentar un objeto, y el estado que puede exhibir en un
momento determinado, gracias a que cada objeto es de un tipo específico definido por una
clase o interfaz.

De acuerdo con lo anterior podemos entonces diferenciar cuatro conceptos básicos:

 Clase. Una clase podría pensarse como una plantilla que describe los comportamientos
que un objeto de esta clase puede soportar y los atributos que definirán el estado del
objeto de la clase (por ejemplo: color, peso, valor, etc.)
 Objeto. Con una clase no se puede interactuar, pues estas juegan el papel de planos,
indicando cómo deben estar constituidos los objetos de esta clase. No se puede volar a
Europa en el plano de un avión, se necesita un avión concreto, un objeto de la clase
avión para poder llevar a cabo este requerimiento. Pasar de la clase al objeto se conoce
como instanciación, y el objeto se convierte en una instancia de la clase que lo define.
Cada objeto tiene su propio estado, y puede presentar todos los comportamientos
definidos en su clase.
 Estado (atributos, variables de instancia). Cada objeto (instancia de una clase), contiene
un conjunto único de variables de instancia definidas en su clase. El valor asignado a
estos atributos o variables de instancia, definen el estado de este objeto en un
momento dado.
 Comportamiento (métodos). Si un objeto puede llegar a presentar un comportamiento
dado, es porque él sabe la forma como ejecutar la(s) tarea(s) que debe llevar a cabo
para demostrar este comportamiento. Por ejemplo, un avión puede volar porque está
diseñado para responder a los requerimientos de los pilotos, ejecutando una serie de
tareas mecánicas que le permiten, finalmente, despegar. Cada clase define métodos con
los cuales las instancias de dicha clase podrán responder a los requerimientos de otros
objetos y presentar los comportamientos esperados. Los métodos son algoritmos, en
donde se define la lógica de las clases.

2 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


Identificadores
Otro aspecto que define un objeto es su identidad, lo que permite diferenciar a un objeto
determinado de otro que pertenezca a la misma clase y que posiblemente pueda tener su
mismo estado. Además de los objetos, todas las clases, métodos y en general todos los
componentes de la aplicación, necesitan un nombre, conocido como identificador. No
cualquier nombre puede ser usado como identificador, hay que seguir un conjunto de reglas
que permitirán definir uno valido. El compilador impedirá identificar a un componente si se
incumple con alguna de estas reglas al definir el identificador de este. Por otro lado, existen
algunas convenciones dentro de la comunidad de programadores Java, estas convenciones
se aplican a los identificadores sirviendo de directriz hacia un buen estilo de programación.
Incumplirlas no supone errores de compilación, pero no contribuye a la legibilidad del código,
y hará más complicado acoplar nuestros diseños a otros módulos disminuyendo la
escalabilidad de nuestro sistema.

Empecemos conociendo las reglas que usa el compilador para reconocer un identificador
legal.

Un identificador puede estar compuesto únicamente por caracteres (codificados bajo


UNICODE), números, signo pesos, y guion bajo (_). Pero no cualquier combinación es válida,
para componer un identificador legal se deben seguir estrictamente las siguientes reglas:

 Los identificadores deben comenzar con una letra, un signo pesos, o un guion bajo
“underscore” (_). El primer carácter de un identificador no puede ser un número.
 Después del primer carácter, un identificador puede estar compuesto de cualquier
combinación de letras, números, signos pesos y guiones bajos (_).
 Los identificadores son sensibles a mayúsculas, “var” y “Var” son identificadores
diferentes.
 No hay límite en el número de caracteres que un identificador puede contener.
 No se puede usar como identificador ninguna de las palabras reservadas de Java
(keywords), las cuales se listan en la siguiente tabla.

Abstract Boolean Break Byte Case Catch


Char Class Const Continue Default do
Doule Else Extends Final Finally float
For Goto If Implements Import Instance of
Int Interface Long Native New Package
Private Protected Public Return Short Static
Strictfp Sper Switch Synchronized This Throw
Throw Trasient try Void Volatile While
Assert Enum

[ HERRAMIENTAS PARA LA 3
PRODUCTIVIDAD ]
Según las anteriores reglas, son ejemplos de identificadores legales:

int ___id;

float $variable;

boolean $$$$$$_$$$$$;

long $150000;

char un_identificador_valido_aunque_un_poco_extenso_para_mi_gusto;

int _;

short _1;

Los siguientes son identificadores ilegales para el compilador de Java:

int id de una variable;

float 2_variable;

long $100.000;

double valor#3;

char 23;

long The_Thorin's_Hall;

boolean –test;

Las reglas para identificadores legales no solo aplican para nombres de variables, también
son validas para los nombres de todos los componentes de Java, como clases, métodos e
interfaces; por esto, la comunidad de programadores hace uso de convenciones, que aunque
no representan reglas para el compilador, incrementan la legibilidad y compatibilidad del
código fuente al diferenciar claramente qué tipo de componente se está utilizando. Ejemplo
de estas convenciones son el uso de una letra mayúscula como primer carácter del
identificador de una clase o interface, o separar las palabras en un identificador compuesto
por más de una, por medio del formato “camelCase”, es decir, la primera letra de cada
palabra compuesta debe ir en mayúscula como enEsteEjemplo.

4 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


Declaración de clases
En esencia, escribir código en Java se limita a construir clases e interfaces, definiendo dentro
de estas clases sus atributos y métodos. Los diferentes comportamientos de los diseños
implementados, dependen de qué forma se definen y declaran las clases, métodos y
atributos. Retomemos algunos de estos aspectos básicos en cuanto a declaración de clases
se trata antes de profundizar en temas más especializados.

 Solo puede haber una clase pública por archivo de código fuente. Si esta clase pública se
define, el nombre de esta clase debe ser el mismo del archivo de código fuente que la
contiene.
 Pueden aparecer comentarios al inicio o final de cualquier línea de código del archivo
fuente, al no ser tenidos en cuenta por el compilador, estos representan una excepción
a las reglas de posicionamiento que se enuncian aquí.
 Si la clase hace parte de un paquete, la declaración de pertenencia a este paquete debe
ser la primera línea de código del archivo.
 Si se necesita importar uno o más paquetes o clases individuales de estos paquetes, las
sentencias de importe import, deben ser ubicadas entre la declaración de paquete y la
declaración de la clase. En caso de que la clase no pertenezca a ningún paquete en
particular, las sentencias import deben ser las primeras en el archivo de código fuente.
 Un archivo de código fuente puede tener más de una clase no pública. Solo puede haber
una clase pública, pero pueden haber más clases además de esta, siempre y cuando no
tengan modificador de acceso público.
 Un archivo de código fuente puede no tener clases públicas, en este caso no es
necesario que el nombre de ninguna de estas coincida con el nombre del archivo que las
contiene.

Las sentencias package e import hacen un uso especial de los nombres completos de las
clases (fully qualified name).

Sentencias import e importaciones estáticas

Usualmente la finalidad de las sentencias import es ahorrar tiempo de codificación,


permitiendo a su vez obtener un código más legible en los casos en donde se necesite
trabajar con clases provenientes de paquetes distintos al de la clase en la que se está
trabajando. El ahorro en el tiempo de codificación, se ve reflejado al no tener que escribir los
nombres completos de las clases (fully qualified name) de paquetes externos cada vez que se
necesite referenciarlas. Esto significa que las sentencias import no son obligatorias cuando se
necesita trabajar con clases de paquetes externos, puesto que siempre es posible

[ HERRAMIENTAS PARA LA 5
PRODUCTIVIDAD ]
referenciarlas usando su nombre completo al momento de usarlas. Por ejemplo, en la
siguiente clase:

public class Test {

public static void main(String[] args) {

java.util.Random aleatorios = new java.util.Random();

java.util.Vector<Integer> numerous;

numeros= new java.util.Vector<Integer>();

for(int i=0; i<20; i++){

numeros.add(aleatorios.nextInt(51));

El uso de los nombres completos de las clases Vector y Random hace innecesario el uso de las
sentencias import para estas clases, aunque es preferible escribirlas y de esta forma
ahorrarse trabajo al escribir los nombres completos de las clases, que usualmente son
bastante largos. A partir de Java 5 se ha incorporado una nueva característica relacionada
con las sentencias import, que pretende ahorrar aún más la codificación necesaria para
trabajar con clases de paquetes externos, esta vez pensando en el uso de miembros
estáticos de estas clases. Esta característica se conoce como importaciones estáticas (statics
imports), y permite importar los miembros estáticos de una clase para ser utilizados
directamente en el código, sin necesidad de referenciar las clases que los definen. Veámoslo
más claro con un ejemplo:

6 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


Sin usar sentencias import:

public class Test {

public static void main(String[] args) {

String in;

java.util.Random aleatorio = new java.util.Random();

javax.swing.JOptionPane.showMessageDialog(null, "Adivine el número");

in = javax.swing.JOptionPane.showInputDialog("Ingrese número de 1 a 5");

int numero = Integer.parseInt(in);

if(numero == aleatorio.nextInt(5)+1)

System.out.println("Adivinó");

else

System.out.println("No adivinó");

Usando sentencias import normales:

(Clases del paquete java.lang no necesitan ser importadas):

import java.util.Random;

import javax.swing.JOptionPane;

public class Test {

public static void main(String[] args) {

String in;

[ HERRAMIENTAS PARA LA 7
PRODUCTIVIDAD ]
Random aleatorio = new Random();

JOptionPane.showMessageDialog(null, "Adivine el número");

in = JOptionPane.showInputDialog("Ingrese número de 1 a 5");

int numero = Integer.parseInt(in);

if(numero == aleatorio.nextInt(5)+1)

System.out.println("Adivinó");

else

System.out.println("No adivinó");

Usando sentencias static import:


import static javax.swing.JOptionPane.*;

import static java.lang.Integer.parseInt;

import static java.lang.System.out;

import java.util.Random;

public class Test {

public static void main(String[] args) {

String in;

Random aleatorio = new Random();

showMessageDialog(null, "Adivine el número");

in = showInputDialog("Ingrese número de 1 a 5");

int numero = parseInt(in);

if(numero == aleatorio.nextInt(5)+1)

out.println("Adivinó");

else

8 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


out.println("No adivinó");

Los tres ejemplos presentan el mismo comportamiento, la única diferencia está en el uso de
las clases de paquetes externos.

Respecto al uso de importaciones estáticas se pueden observar los siguientes


comportamientos:

 La sintaxis para usar un importe estático debe ser como se observa: import static
seguido por el nombre completo (fully qualified name) del miembro estático que se
desee importar, como en el ejemplo el método parseInt de la clase Integer, o el atributo
out de la clase System.
 Si se necesita importar todos los miembros estáticos de una clase, se puede usar un
asterisco en cambio del nombre del miembro particular, como en el ejemplo al importar
todos los miembros estáticos de la clase JOptionPane.

Hay que usar con cuidado esta característica, pues afecta la legibilidad del código, ya que al
utilizar los miembros estáticos directamente, pueden llegar a confundirse con los miembros
propios de la clase que se está definiendo.

Importar miembros estáticos de esta forma puede llevar a referencias ambiguas, por ejemplo
si se importan todos los miembros estáticos de la clase java.lang.Integer y también se
importan todos los miembros estáticos de la clase java.lang.Long, se presentan
ambigüedades como en el caso de la constante MAX_VALUE, en este caso no habría forma de
saber a cuál de los dos miembros se referencia al llamarlos directamente dentro del código,
en casos como este el compilador producirá un error.

Se pueden importar estáticamente objetos (atributos) estáticos, constantes (las constantes


son estáticas y finales), y métodos estáticos.

Modificadores
Al momento de declarar atributos, métodos o clases (incluyendo clases internas que se
tratarán a fondo en otra lectura), se pueden utilizar diversos modificadores que permiten
controlar a fondo el comportamiento y papel del componente que se está definiendo. Los
modificadores pueden catalogarse en dos tipos según si son modificadores de acceso o no.

[ HERRAMIENTAS PARA LA 9
PRODUCTIVIDAD ]
Modificadores de acceso
Al trabajar con los modificadores de acceso se debe tener especial cuidado ya que dadas las
características de estos en Java, pueden ser utilizados para probar la atención del aspirante
en el examen de certificación. Esto se debe a que existen cuatro niveles de acceso, pero solo
tres modificadores. Si no se antepone un modificador de acceso a una declaración, (de
método, atributo o clase) se podría pensar en que simplemente no se quiere controlar el
acceso a ese componente, pero en realidad se está asignando un nivel de control acceso y no
es el menos restrictivo. De esta forma, siempre, cualquier método, clase o atributo de clase
declarado tiene un nivel de acceso controlado, sea explícitamente asignado o no.

Acceso a una clase

En general, cuando desde el código de una clase se desea acceder a otra clase, es porque se
necesita realizar alguna de las siguientes acciones:

 Crear una instancia de la clase


 Extender de la clase
 Acceder a los miembros estáticos de la clase, dependiendo del control de acceso que se
aplique sobre ellos.
El nivel de acceso aplicado a la clase altera directamente la visibilidad de todos sus miembros,
si una clase A no puede ver la clase B dado que esta última tiene un modificador de acceso
restrictivo, no importa que modificadores de acceso tengan los métodos o atributos de la
clase B, la clase A no podrá verlos.

En la declaración de una clase (excluyendo las clases internas ya que son conceptos
distintos) pueden aplicarse dos tipos de modificadores de acceso.

Acceso por defecto (Default)

Una clase con nivel de acceso por defecto no tiene un modificador de acceso explicito en su
declaración, por tanto, este es el nivel de acceso que se obtiene cuando no se especifica
ningún modificador al declarar una clase. Al nivel de control de acceso por defecto también
se le conoce como acceso a nivel de paquete, dado que la clase declarada con este nivel de
control, puede ser vista únicamente por clases que se encuentren en el mismo paquete. Si
una clase de un paquete distinto intenta acceder a una clase con nivel de acceso por defecto
el compilador desplegara un error. Es decir, fuera del paquete en que se declara, esta clase
no podrá ser instanciada o heredada; para cualquier clase externa al paquete, se puede
asumir que la clase ni siquiera existe.

10 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


Acceso público

Una clase que en su declaración utilice el modificador de acceso public provee acceso a todas
las clases en todos los paquetes del diseño de la aplicación. Una clase pública puede ser
instanciada o heredada desde cualquier otra clase, siempre y cuando no existan otros
modificadores como los que analizaremos a continuación.

Otros modificadores
Otros modificadores que se pueden aplicar a la declaración de una clase son: final, abstract, y
strictfp. Estos modificadores, al no estar orientados a restringir el nivel de acceso, pueden ser
usados conjuntamente con los modificadores de acceso descritos anteriormente, aunque no
siempre se pueden mezclar entre ellos. Veamos entonces cuál es la funcionalidad y
comportamiento de estos modificadores que no son de acceso.

Strictfp

Se puede aplicar a la declaración de una clase o de un método pero no de un atributo, e


indica que todos los métodos de la clase marcada, o el método marcado particularmente, se
basan en las reglas del estándar IEEE 754 para la representación de datos de tipo coma
flotante. Sin el modificador, es la plataforma la que determina de qué forma se representan
estos datos.

Final

Al marcar una clase con el modificador final se impide que esta clase sea extendida, es decir,
que no se puede usar como clase padre en una relación de herencia. Al marcar una clase con
este modificador se está indicando que con esta se llegó al nivel final de herencia, esta clase
no se puede extender más, y sus métodos no podrán ser sobrescritos por una versión más
especializada. En el api de Java hay varios ejemplos de este tipo de clases, uno de ellos: la
clase String, la cual está marcada como final en su declaración, por tanto, es un intento de
extenderla, como en el siguiente ejemplo:

public class Cadena extends String {

[ HERRAMIENTAS PARA LA 11
PRODUCTIVIDAD ]
Resultaría en un error del compilador indicando que la clase String no puede ser heredada ya
que se marcó como final en su declaración.

Abstract

Si una clase tiene al menos un método declarado como abstracto (los detalles de esto los
estudiaremos más adelante), la clase debe ser marcada como abstracta lo que impedirá que
se creen instancias de esta clase. Un método abstracto no tiene implementación, es decir no
tiene definido un conjunto de instrucciones que le permitan responder a un llamado. Si una
clase tiene al menos un método de este tipo, ¿qué pasaría si desde un objeto de esta clase se
hiciera un llamado al método abstracto?, ¿de qué forma debería responder la aplicación, si no
tiene definido un conjunto de instrucciones para responder al llamado? Por esto, una clase
marcada como abstracta no puede ser instanciada, su finalidad entonces se reduce a ser
clase padre en una línea de herencia en donde finalmente serán implementados sus métodos
abstractos.

Por ejemplo, en la siguiente clase:

public abstract class Mamifero{

private int edad;

private double peso;

//otros atributos

public abstract void desplazarse(int metros);

//otros métodos

Todo compila perfectamente, aún cuando el método finaliza con un punto y coma y no con
los corchetes que delimitan su implementación, esto gracias a que el método se declaró con
el modificador abstract, claro está. Pero al intentar compilar el siguiente código en alguna
otra clase:

12 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


public static void main (String args[]){

Mamifero flipper = new Mamifero();

El compilador desplegará inmediatamente un error indicando que la clase no puede ser


instanciada ya que ha sido declarada abstracta.

Más adelante detallaremos más aspectos de los métodos abstractos, por ahora nos importa
recalcar que:

 Si una clase contiene en su implementación al menos un método abstracto, esta clase


debe ser marcada como abstracta.
 Una clase abstracta puede tener métodos concretos (aquellos que no son abstractos y,
por tanto, tienen definida su implementación).
 Una clase abstracta puede incluso no tener ningún método abstracto.
 No tiene sentido marcar una clase como abstracta y final simultáneamente. Una clase
abstracta está pensada para ser usada en herencia, pero el modificador final impide que
sea heredada, por tanto, usar estos dos modificadores juntos más que ser una
incoherencia, representa un error, y el compilador por tanto lo evitará.

Las clases abstractas son de gran utilidad a la hora de brindar comportamiento polimórfico a
los objetos, en colaboración con las interfaces, estas clases brindan una gran capacidad de
extensibilidad y flexibilidad a las implementaciones de los diseños orientados a objetos.

Declaración de interfaces
Es posible relacionar objetos de diferentes líneas de herencia a partir de un comportamiento
compartido y de esta forma agruparlos como si todos pertenecieran a un mismo tipo. Por
ejemplo, si se tiene la clase Murciélago que extiende de Mamífero, y la clase Avión que
extiende de Vehículo, ¿cómo agrupar los objetos de estas clases según su único aspecto en
común? No tienen relación alguna de herencia, entonces no se podrían agrupar como
objetos de algún supertipo, como cuando agrupamos peras y manzanas como objetos de la
clase Fruta. Es aquí donde tienen utilidad las interfaces, los aviones y los murciélagos solo
tienen una característica en común, ambos pueden responder al llamado de volar, ambos
pueden volar y, por tanto, son Voladores. Una interfaz, al ser implementada puede
determinar qué es lo que una clase puede hacer, pero no es obligación de ella definir como lo
debe hacer. Podemos entonces, para el ejemplo, crear una interfaz Volador, e indicar que los
objetos de las clases que implementen esta interfaz tienen que ser capaces de volar, mas no

[ HERRAMIENTAS PARA LA 13
PRODUCTIVIDAD ]
define cómo lo deben hacer, esto es responsabilidad de la clase Avión y Murciélago. Al tener
una interfaz Volador, ahora es posible agrupar objetos de diferentes líneas de herencia como
si pertenecieran a un tipo que los define a todos, en este caso el de los objetos que pueden
volar. Si se desea incluir a otro grupo de objetos en esta categoría, la clase de estos objetos
debe implementar la interfaz Volador, y está en la obligación de implementar los métodos
que esta interfaz tenga definidos, entre los cuales debe estar el método volar.

Una interfaz es entonces como una clase totalmente abstracta, por tanto, solo puede definir
en su interior métodos abstractos, que le indican a las clases que las implementan los
comportamientos que deben poder ejecutar. No pueden haber métodos concretos dentro
de una interfaz, por tanto, tampoco se pueden instanciar interfaces. La forma como se
pueden definir los métodos en una interfaz es bastante reducida, tanto que incluso aún si el
programador no lo define explícitamente, el compilador asume siempre un comportamiento
que se basa en las siguientes reglas:

 Todos los métodos de una interfaz son implícitamente públicos y abstractos. Esto
significa que aún cuando no se utilicen los modificadores public y abstract en la
declaración del método, este igualmente seguirá siendo público y abstracto.
 Todas las variables de instancia (atributos) definidos dentro de una interfaz deben se
públicas, estáticas y finales. Esto será de esta forma aun cuando al declarar no se
incluyan los modificadores public, static y final. Significa esto que las interfaces solo
pueden definir constantes como atributos, no variables de instancia en el sentido
estricto de la palabra.
 Los métodos de una interfaz no pueden ser marcados con el modificador static.
 Al ser abstractos los métodos de una interfaz no pueden ser marcados con los
modificadores final, strictfp o native. Más adelante detallaremos estos modificadores a
nivel de métodos.
 A diferencia de las clases, una interfaz puede heredar de una o más interfaces. (Cuidado,
la herencia entre interfaces es solo entre interfaces, una interfaz no puede heredar de
una clase).
 Una interfaz no puede implementar otra interfaz (Las interfaces solo contienen
métodos abstractos, por tanto, no podría implementar los métodos de otra interfaz).
 Al declarar una interfaz se debe usar la palabra clave interface en lugar del conocido
class usado en la declaración de clases.
 Las interfaces representan otra forma para lograr el polimorfismo.

En cuanto a los modificadores a la hora de trabajar con interfaces, se debe dedicar especial
atención y conocer a fondo el funcionamiento de una interfaz y sus miembros. Por ejemplo,
¿sería legal la siguiente declaración de una interfaz?

14 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


public abstract interface Volador{ }

Es legal, innecesario y redundante pero legal. Un interfaz tiene todos sus métodos abstractos,
por tanto, es obvio que esta sea también abstracta, así que implícitamente todas las
interfaces son abstractas, exista o no el modificador abstract en su definición, lo que hace de
su uso algo opcional y redundante en caso de existir.

public abstract interface Volador{ }

public interface Volador{ }

Estas dos declaraciones son legales e idénticas en funcionalidad.

En cuanto a los métodos sucede lo mismo, y hay que estar atentos a posibles preguntas con
truco, por ejemplo:

public interface Volador{

public abstract void volar();

La declaración del método volar, de nuevo es redundante pero legal. Los modificadores
public y abstract no son necesarios en este punto ya que todos los métodos en una interfaz
son implícitamente públicos y abstractos, por tanto, la anterior declaración es equivalente a:

public abstract interface Volador{

void volar();

[ HERRAMIENTAS PARA LA 15
PRODUCTIVIDAD ]
La declaración del método volar no define ningún modificador de acceso, pero cuidado, no
significa en este caso que el método tenga un nivel de acceso por defecto, sino público ya
que implícitamente todos los métodos dentro de una interfaz lo son.

Es muy importante recordar este aspecto, ya que podrá haber entonces muchas
declaraciones distintas que finalmente sean idénticas funcionalmente, gracias a las
declaraciones implícitas de sus modificadores. Por ejemplo, cualquiera de estas cinco
declaraciones podrían incluirse independientemente para el método volar dentro de la
interfaz Volador, todas con idéntica funcionalidad:

void volar();

public void volar();

public abstract void volar();

abstract public void volar();

abstract void volar();

A diferencia de las anteriores declaraciones, las siguientes no compilarían como intento de


declarar el método volar de la interfaz:

final void volar();

Los modificadores final y abstract no pueden ir juntos en una declaración, como lo


explicamos anteriormente en el caso de la declaración de una clase, ocurre lo mismo en un
método, comportamiento que detallaremos más adelante.

static void volar();

No se puede definir métodos estáticos (de clase) en una interfaz.

private void volar();

protected void volar();

16 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


Los métodos en una interfaz siempre deben ser públicos.

Declaración de constantes en una interfaz

Como habíamos dicho anteriormente los atributos de una interfaz son siempre constantes,
garantizando de esta forma que cualquier clase que implemente la interfaz va a tener
invariablemente acceso al mismo valor.

Hay que tener de nuevo especial cuidado con los modificadores implícitos de los atributos
declarados en una interfaz, ya que estos siempre serán:

public static final

Esto implica que el uso explícito de estos modificadores no sea necesario al declarar
atributos en una interfaz. Siempre los atributos declarados en una interfaz son públicos
estáticos y finales aún cuando no se especifiquen los modificadores en la declaración.

De nuevo podemos ver el concepto en los siguientes ejemplos, todas las siguientes
instrucciones individualmente son idénticas funcionalmente para declarar un constante
dentro de una interfaz:

public static int valor = 1;

public int valor = 1;

static int valor = 1;

public int valor = 1;

public static final int valor = 1;

static final int valor = 1;

final static int valor = 1;

final int valor = 1;

int valor = 1;

[ HERRAMIENTAS PARA LA 17
PRODUCTIVIDAD ]
Cualquier combinación de los modificadores requeridos es legal, teniendo en cuenta que
aunque requeridos, también son implícitos, así que pueden obviarse y declarar una constante
sin modificador alguno pero que sigue siendo publica estática y final.

Declarando los miembros de una clase


Una vez analizados los modificadores que se pueden usar al momento de declarar una clase,
nos concentraremos en estudiar los modificadores y demás detalles relacionados con la
declaración de los miembros de una clase, es decir, sus atributos (variables de instancia) y
métodos.

Modificadores de acceso
Al trabajar con métodos y atributos, se dispone de más niveles de control de acceso que al
declarar una clase. A este nivel, además de los ya trabajados, tenemos en total los siguientes
modificadores:

 public
 protected
 default
 prívate

Recordando que default no es un modificador explícito, sino que representa el nivel de
acceso otorgado en este caso a un método o atributo declarado sin utilizar un modificador
de acceso.

En cuanto a modificadores de acceso, las restricciones aplican de la misma forma tanto para
atributos como para métodos, por tanto, nos referiremos a estos de forma conjunta como
miembros de la clase, para poder describir más fácilmente el comportamiento de los
modificadores.

Cuando desde una clase se desea acceder a un miembro de otra clase, debe analizarse la
visibilidad que este miembro declara, pues dependiendo del ámbito de la llamada, puede que
esté restringido el acceso. Un primer aspecto a analizar es el nivel de acceso de la clase, que
incide directamente en el nivel de acceso de sus miembros, por ejemplo si la clase declara un
nivel de acceso por defecto, no importa que sus miembros sean públicos, solo serán visibles
en el ámbito del paquete en donde fue declarada su clase contenedora.

Cabe anotar que los miembros no estáticos de una clase pueden ser accedidos por otra clase
de dos formas distintas:

18 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


 A través de una instancia de la clase que lo define, en un método de otra clase.
 Por medio de herencia, desde una subclase que herede los miembros, que por tanto,
serían accesibles (según el modificador de acceso como veremos a continuación).

Miembros públicos

Cuando un miembro de una clase (sea método o atributo), se declara como público, significa
que cualquier clase, dentro o fuera del paquete de su clase, puede acceder a él, asumiendo
que el modificador de la clase así lo permita.

A través de una instancia de la clase, desde algún método de cualquier clase de cualquier
paquete (si se tiene una instancia es porque la clase también tiene visibilidad), se puede
acceder a los miembros que tengan acceso público sin problemas. Lo mismo sucede cuando
se hereda de la clase, la subclase hereda y, por tanto, tiene acceso a todos los miembros
públicos declarados en la superclase.

Miembros privados

Un miembro declarado como privado, no puede ser accedido desde ninguna otra clase
distinta a la clase en que fue declarado. Significa entonces que el miembro, sea método o
atributo solo existe para servir internamente en la clase en donde se declaró, para el resto de
clases es invisible, no existe. Si la clase que declara miembros privados es extendida, incluso
su subclase no podrá acceder a los miembros privados, es decir los miembros privados no se
heredan. Es importante siempre tener en cuenta que una subclase no puede acceder
directamente a los miembros privados de su superclase.

En una subclase, es posible declarar un método con la misma firma que la de un método
privado de la superclase, pero esto en ningún caso es sobreescritura de métodos (overriding),
pues lo que realmente se está declarando es un nuevo método, que coincide con el nombre,
parámetros y retorno del método privado de su superclase, pero que en ningún momento lo
está redefiniendo, porque para la subclase el método privado no existe. Las reglas de
sobreescritura de métodos no aplican para este caso, los métodos privados no pueden ser
sobrescritos.

Miembros con nivel de acceso por defecto

El nivel de protección de acceso por defecto (default), es el que se asigna a un miembro que
no declare explícitamente un modificador de acceso. Por ejemplo:

[ HERRAMIENTAS PARA LA 19
PRODUCTIVIDAD ]
package paquete;

public class Prueba{

int atributo;

void método() {

//Algunas líneas de código

El atributo y método de esta clase no especifican ningún modificador de acceso en su


declaración, lo que no indica que no tengan nivel de acceso definido; en este caso ambos
miembros tendrán un nivel de acceso por defecto.

El acceso por defecto solo permite acceder a los miembros de una clase desde clases que se
encuentren en el mismo paquete que la clase que los define. Para el resto de clases (aquellas
de paquetes externos) los miembros con nivel de acceso por defecto no existen. Si
intentáramos usar el método definido en la clase anterior en un código como el siguiente:

package otroPaquete;

import paquete;

public class OtraPrueba{

public void otroMétodo(){

Prueba prueba = new Prueba();

prueba.método();

20 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


La clase se puede instanciar a pesar de estar en otro paquete, gracias a la sentencia import y
a que la clase se declaró como publica, pero al intentar acceder a su método, el compilador
arrojaría un error, pues el método tiene restricción de acceso por defecto, y al encontrarse
las clases en distintos paquetes no podrá ser visible para la clase OtraPrueba.

El nivel de acceso por defecto también se le conoce como acceso a nivel de paquete.

Miembros protegidos

Al utilizar el modificador de acceso protected se logra el mismo nivel de restricción a nivel de


paquete (por defecto) con una diferencia. El nivel de acceso protegido permite un acceso a
nivel de paquete y también a través de la línea de herencia de la clase, es decir al declarar un
miembro protegido, este será visible desde cualquier clase en el mismo paquete, o por
subclases así estas se encuentren en paquetes distintos. Pero cuidado, si la subclase se
encuentra en un paquete distinto al de la clase padre que define los miembros protegidos,
estos sólo podrán accederse por medio de la herencia, nunca a través de una instancia de la
clase. Veámoslo con un ejemplo:

package paquete;

public class Prueba{

protected int atributo;

protected void método(){

//algunas instrucciones aquí

Si se quiere acceder a los miembros de la clase Prueba, esto sólo sería posible si el acceso se
efectúa desde una clase en el mismo paquete, o que esté relacionada en línea de herencia
con Prueba. Por ejemplo:

[ HERRAMIENTAS PARA LA 21
PRODUCTIVIDAD ]
package otroPaquete;

import paquete;

public class PruebaHija extends Prueba {

public void test(){

Prueba prueba = new Prueba();

prueba.método();

Cuidado, el método es protected, y se está intentando acceder a él desde una clase en línea
de herencia pero en paquetes distintos. En principio, según las reglas establecidas, parece
que no habría problema, excepto que el acceso al método se hace a través de una referencia
a la clase y no por medio de la herencia, esto hace que el método no sea visible pues el
llamado se encuentra en un paquete diferente. Al tratar de compilar el anterior ejemplo,
resultaría en un error de compilación. Si se quisiera acceder al método protegido en el
ámbito del ejemplo tendría que hacerse por medio de la herencia, modifiquemos la clase
para acceder al método de forma legal:

package otroPaquete;

import paquete;

public class PruebaHija extends Prueba {

public void test(){

this.metodo();

22 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


En este caso se accede al método por medio de la herencia, ya que la clase hereda el método
y, por tanto, puede acceder a él sin importar que se encuentren en paquetes distintos. ¿Qué
pasaría en el llamado de la clase PruebaHija, si se elimina el modificador de acceso del
método en la clase Prueba, y de esta forma se le otorga un nivel de acceso por defecto?

El llamado this.metodo() en el método test de la clase PruebaHija seria ahora también ilegal,
pues el nivel de acceso por defecto impide la visibilidad del método en clases que estén por
fuera del paquete en el que se declaró.

Otros modificadores
Ya hemos analizado el efecto de aplicar modificadores de acceso a los miembros de una clase,
pero de nuevo estos no son los únicos modificadores que se pueden utilizar. Veamos qué
otros modificadores, que no son de acceso, se pueden usar al momento de declarar un
miembro de una clase (método o atributo).

Ya trabajamos con los modificadores final y abstract, los cuales también se pueden aplicar en
algunos miembros, pero además también se dispone de native, transient, synchronized, static
y el también anteriormente mencionado strictfp.

En este punto es necesario tratar a los métodos y atributos de forma separada, ya que aplicar
los modificadores sobre ellos tendrá efectos distintos, o en algunos casos no podrán
aplicarse dependiendo si el miembro es un método o un atributo.

Modificador final sobre métodos

Un método que sea marcado como final, no puede sobrescribirse en el caso de que la clase
que lo declare sea extendida. Es decir, su implementación no puede cambiarse, se utiliza
cuando es necesario proteger la funcionalidad del método. Declarar un método final no
implica que la clase que lo contiene también tenga que ser final, la clase se puede seguir
extendiendo, solo que los métodos marcados como final no podrán sobrescribirse.

Parámetros finales

Las variables locales declaradas dentro de un método no pueden ser marcadas con
modificadores de ningún tipo excepto el modificador final el cual determinaría que la variable
se comportará como una constante y su valor nunca podrá cambiar una vez asignado. Los
parámetros de un método también se comportan internamente como variables locales, y el
modificador final también puede aplicarse sobre ellos, como en el siguiente ejemplo:

[ HERRAMIENTAS PARA LA 23
PRODUCTIVIDAD ]
public boolean verificaEdad ( int edad, final int edadMinima) { }

Al marcar el parámetro edadMinima como final, se evita que su valor sea modificado durante
la ejecución del método, el parámetro siempre mantendrá el valor del argumento pasado en
el momento de la llamada al método.

Métodos abstractos

Un método abstracto se declara pero no se implementa, es decir se define su firma (tipo de


retorno, parámetros, identificador y modificadores), pero no se escribe código alguno en su
interior. Un método abstracto termina su declaración con un punto y coma, ya que no
tendría sentido una llave de apertura de bloque ( { ) puesto que no se va a definir el cuerpo
del método. Al marcar un método abstracto se obliga a que la clase sea extendida, y el
método sea implementado en una subclase. Por ejemplo, se quiere diseñar que los objetos
de la clase Mamífero se pueden “desplazar”, pero no es posible determinar cómo se
desplazan los mamíferos en general (algunos nadan, otros corren o incluso algunos vuelan).
En este caso se puede definir el método “desplazarse” como abstracto y dejar la
responsabilidad de implementarlo a alguna otra clase que extienda de Mamífero, como
indicándole a la clase que quiere heredar, “si usted quiere ser un Mamífero debe
implementar una forma de desplazarse”.

public abstract class Mamifero {

public abstract void desplazarse();

Hay que recordar que una clase que tenga al menos un método abstracto debe declararse
también abstracta.

public class Delfin extends Mamifero{

public void desplazarse(){

System.out.println(“Me voy nadando”);

24 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


}

La clase Delfin al heredar de la clase Mamifero, se ve obligada a implementar el método


desplazarse, a menos que sea declarada abstracta también y espere que otra subclase
implemente el método.

¿La siguiente clase compilaría?

public class Caballo extends Mamifero{

¿Y qué tal esta otra?

public abstract class Murcielago extends Mamifero{

La clase Caballo no compila, pues tiene un método abstracto y no se declaró como abstracta.
¿Cuál método abstracto? Aún cuando la clase está vacía, está heredando de la clase Mamifero
lo cual tiene como consecuencia que el método abstracto también se hereda, y mientras este
no se redefina seguirá siendo abstracto. La clase Murcielago, por otra parte, tampoco
implementa el método abstracto de la clase Mamifero, pero está en cambio se declara como
abstracta, por esto el código compila perfectamente.

Un método jamás puede marcarse como final y abstract simultáneamente; no tendría sentido,
pues el modificador final impide que el método sea redefinido, pero el modificador abstract
exige que el método sea sobreescrito para brindarle una implementación, luego utilizarlos
conjuntamente no tiene sentido y el compilador detecta un intento como este como un error.
Tampoco tiene sentido marcar un método como abstract y private simultáneamente, pues el
modificador private impide que el método sea heredado y, por tanto, sobrescrito para
brindar una implementación, lo que no tiene sentido al tratarse de un método abstracto.

[ HERRAMIENTAS PARA LA 25
PRODUCTIVIDAD ]
Métodos sincronizados (synchronized)

Los métodos sincronizados protegen los datos del acceso concurrente en caso de que
múltiples hilos intenten acceder simultáneamente al método. Al ser sincronizado, un método
solo permite que un hilo lo ejecute a la vez. Para sincronizar un método se usa en su
declaración el modificador synchronized. Por ejemplo:

public synchronized int métodoSincronizado(double valor) { }

El modificador synchronized puede ser usado en forma conjunta con cualquiera de los
modificadores de acceso.

Métodos nativos

Un método marcado como nativo indica que el método ha sido implementado con código
dependiente de la plataforma por lo general escrito en C. No es necesario para los objetivos
del examen conocer cómo se usa este tipo de métodos. Solo basta conocer que para
declarar un método nativo se debe usar la palabra clave native como modificador. El
modificador native solo puede ser usado en la declaración de un método, no en la de una
clase o atributo. Los métodos nativos, así como los abstractos, no proveen una
implementación, por tanto, en vez de cuerpo su declaración finaliza con un punto y coma.

Modificador strictfp sobre métodos

Como anteriormente se dijo, el modificador strictf permite especificar que la representación


de los datos numéricos de coma flotante debe restringirse al estándar IEE 754. Se puede
marcar una clase para indicar que todos los métodos se deben adherir al estándar, o utilizar
el modificador en un método individual para indicar que solo el representará los flotantes
bajo el estándar. Se puede aplicar a métodos o clases pero no a atributos.

Métodos con una lista de argumentos variable (var-args)


Es posible declarar que un método puede recibir un número variable de argumentos, es decir,
que pueda recibir por ejemplo, dos enteros en un llamado, pero que sea posible ingresar

26 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


cinco enteros en otro sin necesidad de sobrecargar el método o declarar el parámetro como
un arreglo.

Las reglas para definir una lista de argumentos variable (var-arg):

 Al definir un parámetro “var-arg”, se debe especificar el tipo de argumento que el


método recibirá ya sea un tipo primitivo o referenciado.
 Para declarar un “var-arg”, se debe escribir luego del tipo un grupo de puntos
suspensivos (…) un espacio y finalmente un identificador para el arreglo que
finalmente contendrá los datos.
 Pueden definirse otros parámetros para un método que declare un “var-arg”, pero el
“varg-arg” debe ser el último parámetro. Solo puede haber un “varg-arg” en la lista de
parámetros de un método.

Los siguientes son ejemplos de declaraciones legales de “varg-args”.

public int método(double… vararg) { }

Recibe 0 o mas double como parámetros.

public double método2(int var1, int… vars) { }

Recibe uno o más enteros como parámetros.

public void método3(String… palabras) { }

Recibe 0 o varios String.

Los siguientes son ejemplos de declaraciones ilegales de “varg-args”

public boolean método4(String var…) { }

El tipo debe anteceder a los puntos suspensivos, error de sintaxis.

public void método5(int… var1, int x, double… var2) { }

[ HERRAMIENTAS PARA LA 27
PRODUCTIVIDAD ]
Solo se puede declarar un “var-arg” por método.

public int método6 (double… args, int var){ }

Cuando se declara un “varg-arg” este debe ocupar el último lugar en la lista de parámetros.

Acerca de los constructores


Todas las clases en Java siempre tienen constructor, incluso si el programador no especifica
uno explícitamente, pues Java en este caso asigna uno por defecto que no recibe parámetros
e inicializa los atributos de la clase con valores por defecto. En cuanto a declaración de clases
se trata, se puede analizar de los constructores, que se comportan como métodos
cualesquiera excepto en:

 Un constructor no define nunca un tipo de retorno.


 Un constructor debe tener como identificador el mismo nombre de la clase en donde se
declara.
 Los constructores no pueden ser estáticos.
 Los constructores no pueden ser abstractos o finales, dado que estos no se heredan, y
por tanto no se pueden sobrescribir.

Declaración de variables
Al declarar una variable, su tipo puede asociarse con dos grandes grupos:

 Tipos primitivos: Son ocho en Java, para valores numéricos enteros: int, long, short, byte
y dentro de estos como representación de los caracteres char; para valores numéricos
con coma flotante: float y double; y para valores booleanos boolean. Datos de este tipo
no pueden considerarse objetos, y constituyen la base de la implementación de un
diseño orientado a objetos en Java.
 Tipos referenciados o por referencia: Una variable de referencia es usada para
referenciar a un objeto. Se declara según la clase del objeto que se desea referenciar.

Declaración de arreglos
Un arreglo en Java es un objeto que guarda múltiples variables de un mismo tipo o de varios
tipos relacionados por línea de herencia. Esto indica que pueden declararse ya sea de tipos
primitivos o de referencias a objetos. Para el examen es importante conocer cómo se

28 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


declaran, construyen e inicializan los arreglos, por el momento hablaremos de su
declaración:

Como se sabe, para declarar un arreglo se debe especificar primero su tipo, seguido de un
par de corchetes ([ ]) que pueden ir antes o después del identificador del arreglo.

Por ejemplo, las siguientes son declaraciones legales de un arreglo:

int arreglo [];

String [] arreglo2;

La diferencia entre ambas formas se hace visible cuando se declara varios arreglos en una
sola instrucción de declaración:

int var [], var2, var3, var4;

Solo var es un arreglo, var2, var3, var4 son variables simples de tipo entero.

int [] var, var2, var3, var4;

En este caso var, var2, var3, var4 son todos arreglos.

Como también se sabe, es posible declarar arreglos multidimensionales, normalmente se


trabaja con matrices o arreglos bidimensionales, una especie de arreglo de arreglos:

double matriz [][];

Cuidado, según lo anteriormente enunciado la siguiente también sería una declaración valida
de una matriz:

[ HERRAMIENTAS PARA LA 29
PRODUCTIVIDAD ]
double [] matriz [];

Multidimensional se refiere a distintas dimensiones no necesariamente dos, así que las


siguientes también son declaraciones validas:

int arregloMulti [][][];

float arregloMultiMulti[][][][][][];

Declaración de atributos
Los atributos o variables de instancia, se definen dentro del cuerpo de una clase, pero por
fuera de cualquier método, puesto que son propiedades de la clase y de su valor depende el
estado de un objeto de la clase. Usualmente se habla de “atributos”, pero los términos
“campos”, “propiedades” o “variables de instancia” son en esencia igualmente validos,
aunque en determinadas situaciones pueden adoptar diferentes significados, que no son de
importancia para el examen. Para el examen, es importante conocer acerca de los atributos:

 Se pueden declarar con cualquiera de los tres modificadores de acceso, o sin ninguno de
ellos para proveer un nivel de control de acceso por defecto.
 Pueden ser marcados con el modificador final o transient.
 No pueden ser marcados como abstract, synchronized, strictfp o native.
 Se pueden marcar con el modificador static aunque en este caso dejan de ser atributos o
variables de instancia para convertirse en variables de clase.

Atributos finales

Al declarar un atributo con el modificador final, hace que en el atributo no pueda modificarse
su valor una vez asignado explícitamente (no por defecto). Es fácil de comprender cuándo se
habla de valores primitivos, pues una vez asignado un valor a un atributo final entero, por
ejemplo 30, ese atributo siempre tendrá el valor 30 asignado y nunca podrá ser cambiado.
Con tipos referencia requiere un poco más de análisis. Estos tipos de datos almacenan
referencias a objetos, así que este valor es el que nunca puede reasignarse, la referencia al
objeto, pero esto no indica que no se puedan modificar los valores de los atributos del objeto
que se está referenciando. Los datos del objeto pueden ser modificados, pero la referencia
no puede ser reasignada. Según esto, no existen objetos finales, solo existen referencias
finales.

30 [ POLITÉCNICO GANCOLOMBIANO EN ALIANZA CON WHITNEY INTERNATIONAL


Atributos transient

Al serializar un objeto, sus atributos (si también son de clases serializables o primitivos), se
transforman para poder ser enviados por un flujo de datos, por ejemplo para guardarlo en un
archivo. Algunos de estos atributos puede que no se desee que hagan parte de la
serialización del objeto, en este caso se marca con el modificador transient aquellos atributos
que se quieren ignorar en el momento de serializar el objeto.

Atributos volátiles

Así como los métodos sincronizados evitan los problemas comunes del acceso concurrente
al trabajar con hilos, los atributos volátiles implementan su propia estrategia para proteger
los datos en este tipo de situación. Al igual que con transient, volatile es un modificador que
se puede aplicar solo a atributos.

Métodos y variables estáticos


Los métodos y variables estáticos son independientes de una instancia, es decir, están
relacionados más directamente con las clases que con los objetos. En otras lecturas más
especializadas se hablará sobre este tema más en profundidad. Por el momento es
trascendental saber que el modificador static puede usarse en:

 Declaración de métodos
 Declaración de atributos
 Clases internas a otra clase, pero no internas a un método
 Bloques de inicialización.
Y que no puede usarse en:

 Constructores
 Clases, (las clases internas son otro concepto)
 Interfaces
 Clases internas a métodos
 Métodos y atributos de clases internas
 Variables locales.

[ HERRAMIENTAS PARA LA 31
PRODUCTIVIDAD ]

También podría gustarte