POO Con Java PDF
POO Con Java PDF
POO Con Java PDF
FACULTAD DE INGENIERIA
PROGRAMACIÓN ORIENTADA
A OBJETOS CON JAVA
Introducción:
Clases y objetos
A partir de aquí podemos decir que un objeto tiene características propias que
llamaremos atributos y comportamientos o acciones propios de esa clase de
objetos que llamaremos métodos.
Como ya hemos dicho un método es una acción que puede realizar un objeto.
Cuando un objeto necesita interactuar con otro lo hace a través de un método.
Figura 1.1
Encapsulamiento.
Un objeto lo hemos representado de la siguiente manera:
Figura 1.2
El encapsulamiento provee una idea poderosa: la modularidad, es decir el
código fuente puede ser escrito y darle mantenimiento de forma independiente
al resto del programa y de los objetos que interactúan con él.
Herencia
Hay diferentes tipos de herencia: los más importantes son simples y múltiples.
La Herencia simple consiste en que una clase sólo puede heredar de otra clase
llamada clase padre o superclase, en la herencia múltiple una clase puede
heredar de una o mas clases.
Figura 1.4
Polimorfismo
Abstracción
Diseñar una clase de una manera muy general para varias clases que hereden
de ella nos proveerá de un mejor diseño como se verá mas adelante en este
curso en el apartado de clases abstractas.
.
Figura 2.1
La Plataforma Java.
La elección del API a utilizar depende del tipo de programa que queremos
realizar, a continuación se muestran varios API (todos ellos bajo la tecnología
Java) y una breve descripción de cada uno.
Para obtener el J2SE que utilizaremos a lo largo del curso basta con entrar a la
pagina de java: http://java.sun.com y descargarlo. Para detalles de la
instalación revisar el apéndice C.
Figura 2.3
El nombre del archivo debe tener el nombre de la clase publica, en este caso el
nombre del archivo corresponde a HolaMundo.java, Java es un lenguaje
sensitivo lo cual quiere decir que hace una distinción entre mayúsculas y
minúsculas, además la extensión siempre debe ser *.java.
HolaMundo: es clase
String: es clase
main(): método main de la clase.
System: clase
out: atributo de la clase System.
println(): método del atributo out.
Como siguiente paso hay que invocar a la maquina virtual de java (JVM) para
que se encargue de interpretarlo, esto se hace mediante la siguiente
instrucción:
C:\> java HolaMundo
Figura 2.4
Todos los tipos primitivos tienen asociada una envoltura o wrapper que se
muestra en la última columna de la tabla anterior, esta envoltura será vista a
detalle en el capitulo 3: clases envolventes.
El recolector de basura
La definición de una clase especifica cómo serán los objetos de dicha clase,
esto es, de que variables y de que métodos constarán.
Veamos un ejemplo, crearemos una clase llamada Alumno de esa clase vamos
a crear un objeto. Ahora codificando esa clase, tenemos el siguiente código:
//Atributos de la clase
String nombre;
String apellidoPaterno;
String apellidoMaterno;
int edad;
String carrera;
int semestre;
String numeroDeCuenta;
Esta sentencia crea un nuevo objeto, con la palabra reservada new, y lo asigna
a la referencia alumno1. Una vez creado el objeto, este contiene una copia de
todos los atributos declarados en la clase siempre y cuando no estén
declarados como static. Cuando ya esta creado el objeto podemos acceder a
un atributo mediante el operador punto (.). En el ejemplo también es
interesante resaltar el uso de operador + para concatenar cadenas.
Ahora vamos a crear otro objeto de la clase Alumno, este nuevo alumno tendrá
como referencia: alumno2.
Atributos y métodos
alumno1.imprimirDatos();
alumno2.imprimirDatos();
}
}
Hemos agregado el método imprimirDatos, este método es llamado por los dos
objetos creados en las instrucciones:
alumno1.imprimirDatos();
alumno2.imprimirDatos();
Cada objeto ejecuta este método el cual, al ser ejecutado por alumno1, imprime
el valor de los atributos de este objeto, es decir de alumno1, cuando es llamado
Podemos notar que la inicialización de los atributos de cada objeto vuelve a ser
prácticamente la misma, ¿Podemos unir estas líneas en un método como se
hizo con el método de imprimirDatos?. La respuesta de nueva cuenta es sí.
Hasta este momento no hemos definido ningún constructor para nuestra clase
Alumno, pero sin embargo hemos estado haciendo uso del constructor por
default. El constructor por default es agregado automáticamente por Java
siempre y cuando la clase no contenga ningún constructor. Otra característica
del constructor por default es que es un constructor que no tiene argumentos.
En la siguiente línea estamos utilizando el constructor por default.
public Alumno3()
{
System.out.println("Creando un nuevo Alumno...");
}
alumno1.imprimirDatos();
alumno2.imprimirDatos();
}
}
public Alumno3(String nom, String apePat, String apeMat, int anios, String car, int sem, String
cuenta)
{
…
}
public Alumno4(String nom, String apePat, String apeMat, int anios, String car, int sem, String
cuenta)
{
System.out.println("Creando un nuevo Alumno...");
nombre=nom;
apellidoPaterno=apePat;
apellidoMaterno=apeMat;
edad=anios;
carrera=car;
semestre=sem;
numeroDeCuenta=cuenta;
}
alumno1.imprimirDatos();
alumno2.imprimirDatos();
}
}
public Alumno4(String nombre, String apellidoPaterno, String apellidoMaterno, int edad, String
carrera, int semestre, String numeroDeCuenta)
{
System.out.println("Creando un nuevo Alumno...");
nombre=nombre;
apellidoPaterno=apellidoPaterno;
apellidoMaterno=apellidoMaterno;
edad=edad;
carrera=carrera;
semestre=semestre;
numeroDeCuenta=numeroDeCuenta;
}
Como puede verse los atributos de cada objeto no fueron inicializados, esto
ocurre debido a que la asignación de variables en el constructor es únicamente
de forma local, es decir la sentencia:
nombre=nombre;
public Alumno6(String nombre, String apellidoPaterno, String apellidoMaterno, int edad, String
carrera, int semestre, String numeroDeCuenta)
{
System.out.println("Creando un nuevo Alumno...");
this.nombre=nombre;
this.apellidoPaterno=apellidoPaterno;
this.apellidoMaterno=apellidoMaterno;
this.edad=edad;
this.carrera=carrera;
this.semestre=semestre;
this.numeroDeCuenta=numeroDeCuenta;
}
alumno1.imprimirDatos();
alumno2.imprimirDatos();
}
}
Cuando se escriben varios constructores para una clase, hay veces en las que
uno quisiera invocar a un constructor desde otro para evitar la duplicación de
código. Esto se puede lograr utilizando la palabra reservada this.
public Alumno7(String nombre, String apellidoPaterno, String apellidoMaterno, int edad, String
carrera, int semestre, String numeroDeCuenta)
{
System.out.println("Creando un nuevo Alumno...");
this.nombre=nombre;
this.apellidoPaterno=apellidoPaterno;
this.apellidoMaterno=apellidoMaterno;
this.edad=edad;
this.carrera=carrera;
this.semestre=semestre;
this.numeroDeCuenta=numeroDeCuenta;
}
alumno1.imprimirDatos();
alumno2.imprimirDatos();
}
}
Pero hay dos situaciones en las que este enfoque no es suficiente. Una es
cuando se desea tener solamente un fragmento de espacio de almacenamiento
para una parte concreta de datos, independientemente de cuántos objetos se
creen, o incluso aunque no se cree ninguno. La otra es si se necesita un
método que no esté asociado con ningún objeto particular de esa clase. Es
decir, se necesita un método al que se puede invocar incluso si no se ha
creado ningún objeto. Ambos efectos se pueden lograr con la palabra clave
static. Al decir que algo es estático se está indicando que el dato o método no
está atado a ninguna instancia de objeto de esa clase. Con los datos y métodos
ordinarios no estáticos, es necesario crear un objeto para poder utilizarlos,
estos métodos y datos que necesitan un objeto para poder ser utilizados
reciben el nombre de variables y métodos de instancia.
Para declarar un dato o un método static, basta con colocar la palabra clave
static antes de la definición, por ejemplo:
//Alumno8.java
public class Alumno8 {
// atributos de la clase
String nombre; // variable de instancia, cada que creas un objeto se crea un copia para cada
objeto
String apellidoPaterno;
String apellidoMaterno;
int edad;
String carrera;
int semestre;
String numeroDeCuenta;
static int numeroDeAlumnos=0;// variable de clase, no se crean copias para cada objeto, es
fija.
public Alumno8(String nombre, String apellidoPaterno, String apellidoMaterno, int edad, String
carrera, int semestre, String numeroDeCuenta)
{
System.out.println("Creando un nuevo Alumno...");
this.nombre=nombre;
this.apellidoPaterno=apellidoPaterno;
this.apellidoMaterno=apellidoMaterno;
this.edad=edad;
this.carrera=carrera;
this.semestre=semestre;
this.numeroDeCuenta=numeroDeCuenta;
numeroDeAlumnos++;// modificamos una variable de clase
}
//Escuela2.java
public class Escuela2 {
Los tipos de datos primitivos tienen asociado una clase “envoltura”. Esto quiere
decir que si se desea hacer un objeto no primitivo para representar ese tipo
primitivo, se hace uso del envoltorio asociado. Por ejemplo:
char c= ‘x’;
Charácter C = new Character(c)
Arreglos.
int[] suma;
o bien:
Son exactamente la misma sentencia, que declara que la referencia suma será
un array de objetos.
Java permite varias formas de inicializar los array, el acceso a cada elemento
de un array se realiza mediante el índice del elemento. He aquí la salida del
programa:
0123456789
La utilidad jar que viene con el JDK de Sun comprime automáticamente los
archivos que se seleccionan. Se invoca en la línea de comandos:
Las opciones son simplemente una colección de letras, los usuarios de linux
notarán una semejanza con las opciones tar. Las opciones son:
Esto crea un fichero JAR llamado miFichero.jar que contiene todos los archivos
de clase del directorio actual, junto con un archive declaración creado
automáticamente.
Manifest-Version: 1.0
Created-By: 1.5.0_02 (Sun Microsystems Inc.)
Main-Class: Escuela2
Nota que hemos especificado la clase que contiene el método main a ejecutar,
guarda este archivo en el mismo directorio de tus clases con el nombre:
MANIFEST.MF
Una de las cosas para las que puede ser útil finalize( ) es para observar el
proceso de recolección de basura. El ejemplo siguiente resume las
descripciones anteriores del recolector de basura:
//Silla.java
//Demostracion de recolector de Basura y
//finalizacion
public class Silla {
//Basura.java
public class Basura {
public static void main(String args[])
{
//Mientras no se haya puesto la bandera,
//hacer sillas y cadenas de texto
while(!Silla.f)
{
new Silla();
new String("Ocupar Espacio");
}
System.out.println("Despues de haber creado todas las sillas: \n"+
"Creadas en total: "+Silla.creadas+", total finalizadas:
"+Silla.finalizadas);
}
}
//Libro.java
//Utilizacion de finalize() para detectar un objeto
// que no ha sido correctamente eliminado
public class Libro {
boolean comprobado=false;
public Libro(boolean comprobar)
{
comprobado=comprobar;
}
void correcto()
{
comprobado=false;
}
public void finalize()
{
if(comprobado)
System.out.println("Error: comprobado");
}
}
//CondicionDeMuerto.java
public class CondicionDeMuerto {
public static void main(String[] args)
{
Libro novela = new Libro(true);
//Eliminacion Correcta
novela.correcto();
//Cargarse la referencia olvidando la limpieza
new Libro(true);
//forzar la recoleccion de basura y finalizacion
System.gc();
}
}
Comentarios y documentación
Una de las partes más interesantes del lenguaje Java es que los diseñadores
no sólo tuvieron en cuenta que la escritura de código era la única actividad
importante, sino que también pensaron en la documentación del código. Esto
se hizo mediante comentarios especiales que se incrustan dentro del código
fuente, sin embargo, es necesaria una sintaxis especial y una herramienta para
extraer esos comentarios.
Sintaxis
/**
* <pre>
* System.out.println(new Date());
* </pre>
*/
También puede usarse HTML como se haría en cualquier otro documento web
para dar formato al propio texto de las descripciones:
/**
* Uno puede <em>incluso</em> insertar una lista:
* <ol>
* <li> Elemento uno
* <li> Elemento dos
* <li> Elemento tres
* </ol>
*/
@see NombreDeClase
@version información-de-version.
@author: Suele ser el nombre del creador pero podría ser cualquier cosa como
la dirección de correo. La forma de uso es:
@author información-del-autor
@return descripción
Ejemplo de documentación.
import java.util.Vector;
/**
* Este es un ejemplo de código comentado<br>
* <h1>Se pueden usar etiquetas de HTML</h1>,
* para darle formato al texto.<br>
* En esta parte, se describe sobre las características, uso, y funcionamiento
* de la clase en general.
* @author PROTECO
*
*/
public class Comentada extends Vector implements Runnable{
/**
* Asi se comentan los atributos
*/
protected String atributo;
/**
* Este es una atributo de clase, y no modificable
*/
static final int VALOR=10;
/**
* Tambien los métodos constructores pueden ir comentados
*
*/
public Comentada(){
}
/**
* Asi se comentan lo métodos. Se da una descripción de lo que hacen
* y se pueden dar algunos ejemplos. Se utilizan
* @param arg1 Comentario del parámetro 1
* @param arg2 Comentario del parámetro 2
* @param num Comentario del parámetro n
* @return Comentario del valor de retorno
* @throws Exception Comentario sobre alguna excepción que regrese
*/
private int Metodo(String arg1,Vector arg2,int num) throws Exception{
return 0;
}
public void run(){
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
Para ejecutar el comando javadoc en un ventana de comandos escribir:
javadoc Comantada.java
String str="";
String str=new String();
Un string nulo es aquél que no contiene caracteres, pero es un objeto de la
clase String. Sin embargo,
String str;
pos=str.indexOf('p', pos+1);
Vemos que una clase puede definir varios métodos con el mismo nombre pero
que tienen distinto número de parámetros o de distinto tipo, esto se conoce
como sobrecarga de métodos y será visto a detalle en el siguiente capitulo.
Comparación de Strings
Esta porción de código devolverá que str1 y str2 son distintos objetos pero con
el mismo contenido. str1 y str2 ocupan posiciones distintas en memoria pero
guardan los mismos datos.
Cambiemos la segunda sentencia y escribamos:
Los objetos str1 y str2 guardan la misma referencia al objeto de la clase String
creado. La expresión (str1==str2) devolverá true. Así cuando deseamos
comparar el contenido de dos String se debe usar el método equals, ya que de
otra forma, con el operador = = compararemos si los Objetos apuntan a la
misma dirección de memoria.
String str="Tomás";
int resultado=str.compareTo("Alberto");
La variable entera resultado tomará un valor mayor que cero, ya que Tomás
está después de Alberto en orden alfabético.
String str="Alberto";
int resultado=str.compareTo("Tomás");
La variable entera resultado tomará un valor menor que cero, ya que Alberto
está antes que
Tomás en orden alfabético.
int valor=10;
String str=String.valueOf(valor);
Cuando se hereda, se dice: “Esta clase nueva es como esa clase vieja”. Se
dice esto en el código dando el nombre de la clase pero antes de abrir el
cuerpo de clase, se pone la palabra reservada extends seguida del nombre de
la clase base. Cuando se hace esto, automáticamente se tiene todos los
atributos y métodos de la clase base. He aquí un ejemplo.
//Animal.java
public class Animal {
String raza;
String nombre;
int edad;
String tamaño;
String tipoDePelaje;
public Animal(){
System.out.println("El animal es totalmente Rebelde");
}
//Gato.java
public class Gato extends Animal {
int numVidas=7;
String tipoDeComida;
public void maullar() {
System.out.println("El gato esta maullando");
Gato creado
El animal respira
El gato esta maullando
El gato esta rasgando
El animal duerme
Como ya dijimos, una vez creado un objeto de tipo Gato se creará, en primera
instancia, un objeto tipo Animal, el programa anterior crea este objeto por
medio del constructor por default, pero ¿Por qué no crear el objeto Animal
llamando otro constructor? Si usamos el segundo constructor de animal
podemos inicializar las variables nombre y raza de manera automática, para
//Animal.java
public class Animal2 {
String raza;
String nombre;
int edad;
String tamaño;
String tipoDePelaje;
public Animal2(){
System.out.println("El animal es totalmente Rebelde");
}
}
}
Gato creado
El animal respira
El gato esta maullando
El gato esta rasgando
El animal duerme
//SobreCarga.java
public class SobreCarga{
public SobreCarga(){
System.out.println("Soy un objeto creado por el constructor sin argumentos");
}
public SobreCarga(int num){
System.out.println("Soy un objeto creado por el constructor con un argumento
int= "+num);
}
public SobreCarga(String cad){
Sobrescritura de Métodos.
Una subclase hereda todos los métodos de su superclase que son accesibles a
dicha subclase a menos que la subclase sobrescriba los métodos.
//Gato3.java
public class Gato3 extends Animal2 {
int numVidas=7;
String tipoDeComida;
public void maullar() {
System.out.println("El gato esta maullando");
}
public void rasgar() {
System.out.println("El gato esta rasgando");
}
public Gato3(String nombre,String raza) {
super(nombre, raza);
System.out.println("\nGato creado");
}
}
}
He aquí la Salida:
En el constructor llamado por super
Gato creado
El animal respira
El gato esta maullando
El gato esta rasgando
El gato esta durmiendo, no molestar
super.dormir();
Justo antes de la línea que imprime: “El gato esta durmiendo, no molestar”. Es
decir:
//Instrumento.java
//Herencia y upcasting
public class Instrumento{
//Viento.java
//Los instrumentos de Viento son instrumentos!
//porque tienen la misma interfaz
public class Viento extends Instrumento{
public static void main(String[] args){
Viento flauta=new Viento();
Instrumento.tune(flauta); //Upcasting
}
}
Vemos que el upcasting tiene sentido ya que partimos de una clase más
específica a una más general, por lo que se cumple la regla. La clase hija podrá
tener más métodos que la clase padre, sin embargo, debe contener por lo
menos los métodos de la clase padre.
Para Datos
Una variable o atributo puede ser una constante que deseamos no cambia
nunca en nuestro programa, esto se puede realizar anteponiendo la palabra
final a la declaración de una variable. Utilizar constantes declaradas por final
puede eliminar parte de la sobrecarga en tiempo de ejecución de un programa.
Al usar final con referencias a objetos en vez que con datos primitivos, su
significado se vuelve algo confuso. Con un dato primitivo, final convierte el
valor en constante, pero con una referencia a un objeto, final hace de la
referencia una constante. Una vez que la referencia se inicializa a un objeto,
ésta nunca se puede cambiar para que apunte a otro objeto. Sin embargo se
puede modificar el objeto en sí; Java no proporciona ninguna manera de
convertir un objeto arbitrario en una constante.
//Valor.java
public class Valor {
int i = 1;
}
//DatosConstantes.java
public class DatosConstantes {
//Pueden ser constantes en tiempo de compilacion
final int variable1 = 9;
static final int VAL_DOS=99;
//Tipica constante publica
public static final int VAL_TRES=39;
//No pueden ser constantes en tiempo de compilacion
final int variable4= (int)(Math.random()*20);
static final int variable5=(int)(Math.random()*20);
fd1.escribir("fd1");
System.out.println("Creando un nuevo DatosConstantes");
DatosConstantes fd2= new DatosConstantes();
fd1.escribir("fd1");
fd2.escribir("fd2");
}
}
Nota que los datos primitivos static final, se escriben en mayúsculas por
convención. Una posible salida del programa se muestra a continuación:
Fíjese que los valores de variable4 para fd1 y fd2 son únicos, pero el valor
variable5 no ha cambiado al crear el segundo objeto DatosConstantes. Esto
es porque es estático y se inicializa una vez en el momento de la carga y no
cada vez que se crea un nuevo objeto.
Constantes Blancas.
//ConstanteBlanca.java
public class ConstanteBlanca {
public ConstanteBlanca(){
j=1; //Inicializa la constante blanca
p= new Elemento();
}
public ConstanteBlanca(int x){
j=x;//Inicializa la constante blanca
p=new Elemento();
}
public static void main(String args[])
{
ConstanteBlanca bf= new ConstanteBlanca();
}
}
Hay dos razones que justifican los métodos constantes. La primera es poner un
“bloqueo” en el método para evitar que cualquier clase heredada varíe su
significado. Esto se hace por razones de diseño cuando uno se quiere asegurar
de que se mantenga el comportamiento del método durante la herencia,
evitando que sea sobrescrito.
Para que un método sea constante, basta con anteponer la palabra final en su
firma, ejemplo:
Clases Constantes
//Cerebro.java
public class CerebroPequenio {
//Dinosaorio.java
public final class Dinosaurio {
int i=7;
int j=1;
CerebroPequenio x= new CerebroPequenio();
public void f()
{}
}
//Jurasico.java
public class Jurasico {
public static void main(String args[])
{
Dinosaurio n= new Dinosaurio();
//Nota.java
//Herencia y upcasting.
public class Nota{
private int valor;
private Nota(int val){ valor=val;}
public static final Nota DO_MAYOR=new Nota(0);
public static final Nota DO_SOSTENIDO=new Nota(1);
public static final Nota SI_BEMOL =new Nota(2);
}
//Instrumento2.java
public class Instrumento2{
public void play(Nota n){
System.out.println("Instrumento.play()");
}
}
//Viento2.java
//Los instrumentos de viento son también tipo Instrumento!
//porque tienen el mismo comportamiento.
public class Viento2 extends Instrumento2{
//Redefiniendo un método
public void play(Nota n){
System.out.println("Viento.play()");
}
}
//Musica2.java
public class Musica2{
public static void tonada(Instrumento2 i){
i.play(Nota.DO_SOSTENIDO);
}
public static void main(String[] args){
Viento2 flauta=new Viento2();
tonada(flauta); //Upcasting
}
}
//Metal.java
public class Metal extends Instrumento2{
//Redefiniendo un método
public void play(Nota n){
System.out.println("Metal.play()");
}
}
//Cuerda.java
public class Cuerda extends Instrumento2{
//Redefiniendo un método
public void play(Nota n){
System.out.println("Cuerda.play()");
}
}
//Musica3.java
public class Musica3{
public static void tonada(Viento2 v){
v.play(Nota.DO_SOSTENIDO);
}
public static void tonada(Cuerda c){
c.play(Nota.DO_SOSTENIDO);
}
public static void tonada(Metal m){
m.play(Nota.DO_SOSTENIDO);
}
public static void main(String[] args){
Viento2 f=new Viento2();
Cuerda c=new Cuerda();
Metal p=new Metal();
tonada(f);
tonada( c );
tonada( p );
}
}
Como podemos observar, en este ejemplo creamos un método para cada tipo
de objeto distinto. Nuestra primera impresión es que dicho código resulta
bastante largo debido a que repetimos una y otra vez la misma función.
Además resulta muy estático, dado que una vez que creemos un nuevo objeto
que herede de la clase Instrumento, tendremos que agregar una nueva
Todos los métodos en Java utilizan dicha técnica a menos que sean declarados
final. Dicho de otra forma, Java es capaz de reconocer el tipo de objeto que
será utilizado en un método gracias al dynamic binding.
f.Dibujar();
//Figura.java
//Polimorfismo en Java
public class Figura{
void dibujar()
{
System.out.println("Dibujando una Figura");
}
void borrar()
{
System.out.println("Borrando una Figura");
}
}
//Triangulo.java
public class Triangulo extends Figura{
void dibujar(){
System.out.println("Triangulo.dibujar()");
}
void borrar(){
System.out.println("Triangulo.borrar()");
}
}
//Cuadrado.java
public class Cuadrado extends Figura{
void dibujar(){
System.out.println("Cuadrado.dibujar()");
}
void borrar(){
System.out.println("Cuadrado.borrar()");
}
}
Si se tiene una clase abstracta como instrumento, los objetos de esa clase casi
nunca tienen significado. Es decir, Instrumento simplemente tiene que
expresar la interfaz, y no una implementación particular, de forma que no tiene
sentido crear objetos de tipo Instrumento.
abstract void f ( );
//Instrumento4.java
//Clases abstractas y métodos.
abstract class Instrumento4{
int i;
public abstract void play();
public String quienSoy(){
return "Instrumento";
}
public abstract void adjust();
}
//Viento4.java
public class Viento4 extends Instrumento4{
public void play(){
System.out.println("Viento.play()");
}
public String quienSoy(){
//Cuerda.java
public class Cuerda4 extends Instrumento4{
public void play(){
System.out.println("Cuerda.play()");
}
public String quienSoy(){
return "Cuerda";
}
public void adjust(){}
}
//Percusion.java
public class Percusion4 extends Instrumento4{
public void play(){
System.out.println("Percusion.play()");
}
public String quienSoy(){
return "Percusion";
}
public void adjust(){}
}
//Flauta.java
public class Flauta4 extends Viento4{
public void play(){
System.out.println("Flauta.play()");
}
public String quienSoy(){
return "Flauta";
}
public void adjust(){ System.out.println("Flauta.adjust()");}
}
//Saxofon.java
public class Saxofon4 extends Viento4{
public void play(){
System.out.println("Saxofon.play()");
}
public String quienSoy(){
return "Saxofon";
}
}
//Musica4.java
public class Musica4{
static void tune(Instrumento4 i){
i.play();
}
static void tuneAll(Instrumento4[] e){
for(int i=0;i<e.length;i++)
tune(e[i]);
}
public static void main(String[] args){
Interfaces.
Una interfaz dice: “Ésta es la apariencia que tendrán todas las clases que
implementen esta interfaz”. Por consiguiente, cualquier código que use una
interfaz particular sabe qué métodos deberían ser invocados por esa interfaz, y
eso es todo.
Para crear una interfaz, se usa la palabra clave interface en vez de la palabra
clave class. Para hacer una clase que se ajuste a una interfaz particular (o a
un grupo de interfaces), se usa la palabra clave implements. Se esta diciendo
“La interfaz contiene la apariencia, pero ahora voy a decir cómo funciona”. Por
lo demás, es como la herencia. El ejemplo de los instrumentos utilizando
interfaces se muestra a continuación.
//Insterface.java
//Interfaces
interface Instrumento5{
int i=5;
//Los métodos no deben estar definidos.
void play();
String quienSoy();
void adjust();
}
//Viento5.java
public class Viento5 implements Instrumento5{
public void play(){
System.out.println("Viento.play()");
}
public String quienSoy()
{return "Viento";}
public void adjust(){}
}
//Percusion5.java
public class Percusion5 implements Instrumento5{
public void play(){
System.out.println("Percusion.play()");
}
public String quienSoy(){return "Percusion";}
public void adjust(){}
}
//Flauta.java
public class Flauta5 extends Viento5{
public void play(){
System.out.println("Flauta.play()");
}
public String quienSoy(){
return "Flauta";
}
public void adjust(){ System.out.println("Flauta.adjust()");}
}
//Saxofon.java
public class Saxofon5 extends Viento5{
public void play(){
System.out.println("Saxofon.play()");
}
public String quienSoy(){
return "Saxofon";
}
}
//Musica5.java
public class Musica5{
static void tune(Instrumento5 i){
i.play();
}
static void tuneAll(Instrumento5[] e){
for(int i=0;i<e.length;i++)
tune(e[i]);
}
public static void main(String[] args){
Instrumento5[] orquesta=new Instrumento5[5];
int i=0;
orquesta[i++]=new Viento5();
orquesta[i++]=new Percusion5();
orquesta[i++]=new Cuerda5();
orquesta[i++]=new Flauta5();
orquesta[i++]=new Saxofon5();
tuneAll(orquesta);
}
}
//Monstruo.java
//Extendiendo una interfaz con herencia
public interface Monstruo{
void amenaza();
}
//MosntruoPeligroso.java
public interface MonstruoPeligroso extends Monstruo{
void destruye();
}
//Letal.java
public interface Letal{
void mata();
}
//Vampiro.java
interface Vampiro extends MonstruoPeligroso,Letal{
void ChupaSangre();
}
//Dragon.java
public class Dragon implements MonstruoPeligroso{
public void amenaza(){}
public void destruye(){}
}
//ShowDeHorror.java
public class ShowDeHorror {
static void u(Monstruo b){ b.amenaza();}
static void v(MonstruoPeligroso d){
d.amenaza();
d.destruye();
}
public static void main(String[] args){
Dragon barney=new Dragon();
u(barney);
v(barney);
}
}
Clases Internas:
//Paquete1.java
//Creando clases internas
public class Paquete1{
class Contenidos{
private int i=11;
public int valor(){ return i;}
}
class Destino{
private String etiqueta;
Destino(String adonde){
etiqueta=adonde;
}
String leerEtiqueta(){ return etiqueta;}
}
public void enviar(String dest){
Contenidos c=new Contenidos();
Destino d=new Destino(dest);
System.out.println(d.leerEtiqueta());
}
public static void main(String[] args){
Paquete1 p=new Paquete1 ();
p.enviar("Tanzania");
}
}
Las clases internas, cuando se usan dentro de enviar tiene la misma apariencia
que muchas otras clases. Aquí, la única diferencia práctica es que los nombres
se anidan dentro de Paquete1. Generalmente, la clase externa tendrá un
método que devuelva una referencia a una clase interna, como esta:
//Paquete2.java
//Creando clases internas
public class Paquete2{
class Contenidos{
private int i=11;
public int valor(){ return i;}
}
class Destino{
private String etiqueta;
Destino(String aDonde){
etiqueta=aDonde;
}
String leerEtiqueta(){ return etiqueta;}
}
public Destino para(String s)
{
return new Destino(s);
}
public Contenidos cont()
• Paquete
package nombrePaquete;
Donde nombrePaquete puede constar de una sola palabra o de una lista de nombres de
paquetes separados por puntos.
Ejemplo
package miPaquete;
class MiClase
{
...
}
Ejemplo
package nombre1.nombre2.miPaquete;
class TuClase
{
...
}
De esta manera, cuando se requiera hacer uso de estas clases se tendrán que importar de
la siguiente manera.
Ejemplo
import miPaquete.MiClase;
import nombre1.nombre2.miPaquete.TuClase;
class OtraClase
{
/* Aqui se hace uso de la clase 'Miclase' y de la
clase 'TuClase' */
...
}
import miPaquete.*;
Si no se utiliza la sentencia package para indicar a que paquete pertenece una clase, ésta
terminará en el package por default, el cual es un paquete que no tiene nombre.
Ejemplo:
package paquete1;
Donde el punto (.) a indica que se crearan los directorios a partir del directorio actual.
Modificadores de Acceso
Los modificadores más importantes desde el punto de vista del diseño de clases y
objetos, son los que permiten controlar la visibilidad y acceso a los métodos y variables
que están dentro de una clase.
Uno de los beneficios de las clases, es que pueden proteger a sus variables y métodos
(tanto de instancia como de clase) del acceso desde otras clases.
Java soporta cuatro niveles de acceso a variables y métodos. En orden, del más público
al menos público son: público (public), protegido (protected), sin modificador (también
conocido como package) y privado (private).
Subclase en
SI SI SI NO
el mismo paquete
No-Subclase en
SI SI SI NO
el mismo paquete
Subclase en
SI SI/NO (*) NO NO
diferente paquete
No-Subclase en
diferente paquete SI NO NO NO
(Universo)
(*) Los miembros (variables y metodos) de clase (static) si son visibles. Los miembros
de instancia no son visibles.
Como se observa de la tabla anterior, una clase se ve a ella misma todo tipo de variables
y métodos (desde los public hasta los private); las demas clases del mismo paquete (ya
sean subclases o no) tienen acceso a los miembros desde los public hasta los sin-
modificador. Las subclases de otros paquetes pueden ver los miembros public y a los
miembros protected, éstos últimos siempre que sean static ya de no ser así no serán
visibles en la subclase (Esto se explica en la siguiente página). El resto del universo de
clases (que no sean ni del mismo paquete ni subclases) pueden ver sólo los miembros
public.
Ejemplo:
//ClaseInicial.java
package paq01;
public class ClaseInicial
{
public int a = 1;
protected int b = 2;
int c = 3; //es amigable
private int d = 4;
// ClaseHijaEnDiferentePaquete.java
package paq02;
import paq01.*;
public class ClaseHijaEnDiferentePaquete extends ClaseInicial
{
public static void main(String arg[])
{
ClaseInicial obj = new ClaseInicial();
System.out.println("a = " + obj.a);
obj.metodoPublic();
/*
System.out.println("b = " + obj.b);
obj.metodoProtected();
System.out.println("c = " + obj.c);
obj.metodo();
System.out.println("d = " + obj.d);
obj.metodoPrivate();
*/
}
}
// ClaseHijaEnMismoPaquete.java
package paq01;
Una de las características más atractivas del Hot Java fue su soporte para los
"applets", que son las partes del código Java que pueden ser cargadas
mediante una red de trabajo para después ejecutarlo localmente y así lograr o
alcanzar soluciones dinámicas en computación acordes al rápido crecimiento
del ambiente Web.
Durante ese mismo mes, Java Soft dio a conocer el Java Developmet Kit (JDK)
1.0, una rudimentaria colección de componentes básicos para ayudar a los
usuarios de software a construir aplicaciones de Java. Dicha colección incluía
el compilador Java, un visualizador de applets, un debugger prototipo y una
máquina virtual Java(JVM), necesaria para correr programas basados en Java,
también incluía paquetería básica de gráficos, sonido, animación y trabajo en
red.