Autor: Luis Fernando Apáez Álvarez
-Curso PyM-
Clase 4: POO parte II
Fecha: 04 de diciembre del 2022
No podremos modificar variables desde métodos externos:
xxxxxxxxxxpackage PasoPorValor;
public class PasoPorValor{ /*Lo que haremos sera utilizar otro metodo dentro del metodo main * Lo cual se logra pasando una copia de x como parametro del metodo cambiarValor * Pero no podemos modificar la variable x desde metodos externos */ public static void main(String[] args) { int x = 10; System.out.println("x = " + x);
cambioValor(x); }
// Definimos otro método public static void cambioValor(int arg1){ System.out.println("arg1 = " + arg1); arg1 = 15; }}xxxxxxxxxxx = 10arg1 = 10
Inicialmente el valor de la variable x es de 10, luego utilizamos el método cambioValor() en el cual se mandará a imprimir el valor del parámetro que pasemos a dicho método, pero además vemos que en la definición de ese método hacemos el intento por cambiar el valor de argumento. No obstante, el valor de la variable x permanece siendo el mismo, esto es, no podemos cambiar los valores de una variable desde método externos. Lo que ocurre es que en cambioValor(x) se está pasando a dicho método una copia de la variable x y no a dicha variable como tal.
Decimos entonces que en el paso por valor, el valor de un parámetro de la función se copia en otra variable.
Para el siguiente ejemplo utilizaremos la clase Persona:
package clases;
public class Persona { // Atributos: // String nombre; // String apellido; /*Lo que haremos a continuacion sera hacer que los atributos sean publicos * para que podamos acceder a ellos desde afuera de la clase. */ public String nombre; public String apellido;
// Metodos: // Imprime en consola el nombre y apellido public void desplegarInfo(){ System.out.println("Nombre: " + nombre + "\nApellido: " + apellido); }}// Fin de la clase personaLa cual se encontrará almacenada en una carpeta denominada clases. Luego, consideremos
xxxxxxxxxxpackage PasoPorValor;
// Importamos la clase Persona de la clase clasesimport clases.Persona;
public class PasoPorReferencia { /* Tiene que ver con objetos. * Utilizaremos la clase persona para este ejemplo, por lo cual debemos importarla */ public static void main(String[] args) { Persona persona1 = new Persona(); // Asignamos valores a los atributos de la clase persona persona1.nombre = "Juan"; persona1.apellido = "Perez"; System.out.println(persona1.nombre + " " + persona1.apellido);
// Utilizamos el metodo cambioValor cambiaValor(persona1); System.out.println(persona1.nombre + " " + persona1.apellido); }
// Metodo que recibe un objeto persona de la clase Persona public static void cambiaValor(Persona persona) { persona.nombre = "Karla"; }
/* En este caso si pudimos cambiar el valor del atributo nombre del objeto * persona1 desde un metodo externo al main */}xxxxxxxxxxJuan PerezKarla Perez
donde en el paso por referencia, se pasan/copian los parámetros reales a la función.
Tendremos entonces que, en Java, todas las variables primitivas, como un entero o un carácter, almacenan los valores reales y por ello el paso será por valor. Por el contrario, las variables no primitivas, como una clase o un objeto, almacenan las variables de referencia que contienen las direcciones de los objetos a los que se hace referencia, y por ende el tipo de paso será por referencia.
xxxxxxxxxxpackage contexto_estatico;
public class persona { // Atributos: private int idPersona; private String nombre;
// Atributo estatico: se asocia con la clase, no con los objetos private static int contadorPersona; // Getters y Setters para el atributo estatico contadorPersona public static int getContadorPersona() { return persona.contadorPersona; } public static void setContadorPersona(int contadorPersona) { persona.contadorPersona = contadorPersona; }
// constructor: public persona(String nombre) { this.nombre = nombre; // Incremenar el contador por cada objeto. // Como es un atributo estatico, no utilizamos la palabra this (que hace // referencia al objeto), en cambio utilizamos el propio nombre de la clase persona.contadorPersona++;
// Asignar el id de la persona: this.idPersona = persona.contadorPersona; }
// Getters y setters // Atributo nombre public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } // Atributo idPersona public int getIdPersona() { return idPersona; } public void setIdPersona(int idPersona) { this.idPersona = idPersona; }
// Metodo toString: // Es una anotacion que agrega info extra al metodo toString // por ello, se esta sobrecargando el metodo toString public String toString() { return "Persona [idPersona=" + idPersona + ", nombre=" + nombre + "]"; }}y realizamos una prueba
package contexto_estatico;
public class pruebaPersona {
private int cont;
public static void main(String[] args) { // Crear un objeto de la clase persona: persona p1 = new persona("Luis"); // Invocamos el metodo toString: System.out.println("p1: " + p1);
// Creamos otro objeto de la clase persona: persona p2 = new persona("Pedro"); System.out.println("p2: " + p2);
// Mandamos a llamar el metodo imprimir imprimir(p1);
// No podemos utilizar la variable cont pues no es estática // Marcara error: System.out.println(cont);
System.out.println(p1.getContadorPersona()); }
public static void imprimir(persona persona){ System.out.println("persona: " + persona); } // Podemos utilizar la variable cont dentro de un metodo no estatico // Se conoce como metodo dinamico public int getCont(){ // Dentro de un metodo no estatico, podemos invocar un metodo // estatico, pero no a la inversa imprimir(new persona("Carlos"));
// Aqui si podemos utilizar la variable cont return this.cont; }}xxxxxxxxxxp1: Persona [idPersona=1, nombre=Luis]p2: Persona [idPersona=2, nombre=Pedro]persona: Persona [idPersona=1, nombre=Luis]2
Como nota, dado que el método principal main siempre debe ser estático, entonces dentro del método main sólo podemos mandar a llamar métodos estáticos.

El diagrama anterior es un diagrama de clases en el cual la palabra extends nos dice que la clase Persona extiende a las clases Empleado y Cliente, esto es, la clase Persona en la clase padre y las clases Empleado y Cliente son las clases hijas. Al inicio de cada diagrama de clases colocamos los atributos y el tipo de datos de éstos; luego colocaremos los métodos con el tipo de dato que reciben y el tipo de dato de salida. Además, utilizamos el símbolo de + para indicar que los atributos/métodos son públicos y colocamos - para indicar que los atributos/métodos son privados. El diagrama anterior representa un paso antes de llevar a código nuestras clases y corresponde propiamente al diseño de clases. Después del diseño se lleva acabo la implementación en código. De tal manera comenzaremos por definir la clase padre Persona en una carpeta nueva
xxxxxxxxxxpackage Herencia;
public class Persona { // Atributos protegidos (se utilizan en herencia) /* Si los atributos se heredaran a otras clase, * entonces el tipo debe ser protected y no private * o public */ protected String nombre; protected char genero; protected int edad; protected String direccion; // Constructor /* Lo siguiente nos permite inicializar los objetos * de formas distintas */ public Persona(){ } public Persona(String nombre){ this.nombre = nombre; } public Persona(String nombre, char genero, int edad, String direccion){ this.nombre = nombre; this.genero = genero; this.edad = edad; this.direccion = direccion; } // Getters y Setters public String getNombre() { return this.nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public char getGenero() { return this.genero; } public void setGenero(char genero) { this.genero = genero; } public int getEdad() { return this.edad; } public void setEdad(int edad) { this.edad = edad; } public String getDireccion() { return this.direccion; } public void setDireccion(String direccion) { this.direccion = direccion; }
// Metodo toString public String toString() { return "Persona{" + "nombre='" + nombre + '\'' + ", genero=" + genero + ", edad=" + edad + ", direccion='" + direccion + '\'' + '}'; }}donde los atributos/métodos protegidos son visible en la clase en donde se define y en cualquiera de sus subclases, pero no fuera de ellos.
Procedemos a definir las clases hijas
xxxxxxxxxxpackage Herencia;
// colocamos extends para que herede de la clase Persona
public class Empleado extends Persona { // Atributos privados private int idEmpleado; private double sueldo;
// Atributo estatico para el id /* El contexto estático asocia al atrubuto/método * directamente con la clase y no con los * objetos. */ private static int contadorEmpleado;
// Getter y Setter para el atributo estatico public static int getContadorEmpleado() { return Empleado.contadorEmpleado; } public static void setContadorEmpleado(int contadorEmpleado) { Empleado.contadorEmpleado = contadorEmpleado; } /* En java los constructores no se heredan, para acceder * a ellos debemos utilizar el metodo super() */
// Constructor utilizando todos los parametros public Empleado(String nombre, char genero, int edad, String direccion, double sueldo) { // Atributos provenientes de la clase padre super(nombre, genero, edad, direccion);
// Atributos propios de la clase hija. // Preaumento (++): primero lo utiliza y después le aumenta this.idEmpleado = ++Empleado.contadorEmpleado; this.sueldo = sueldo; }
// Getters y Setters public int getIdEmpleado() { return this.idEmpleado; } public double getSueldo() { return this.sueldo; } public void setSueldo(double sueldo) { this.sueldo = sueldo; }
// Metodo toString // Indica que estamos sobreescribiendo el metodo toString de la clase padre public String toString() { // Alternativa a la concatenacion de strings StringBuilder sb = new StringBuilder(); sb.append("Empleado{"); sb.append("idEmpleado=").append(this.idEmpleado); sb.append(", sueldo=").append(this.sueldo); sb.append(", Persona{").append(super.toString()); sb.append("}}"); return sb.toString(); }
}xxxxxxxxxxpackage Herencia;
// Importamos la clase necesaria para trabajar con fechasimport java.util.Date;
public class Cliente extends Persona { // Atributos privados private int idCliente; private Date fechaRegistro; private boolean vip;
// Atributo estatico para el id private static int contadorCliente;
// Getter y Setter para el atributo estatico public static int getContadorCliente() { return Cliente.contadorCliente; } public static void setContadorCliente(int contadorCliente) { Cliente.contadorCliente = contadorCliente; }
// Constructor public Cliente(String nombre, char genero, int edad, String direccion, boolean vip) { // Atributos provenientes de la clase padre super(nombre, genero, edad, direccion);
// Atributos propios de la clase hija this.idCliente = ++Cliente.contadorCliente; /* Para crear un objeto de la clase fecha * escribimos new Date(), si no le damos ningun parametro * tomara la fecha actual. Si queremos colocar una fecha * en especifico colocamos, por ejemplo, new Date(116, 5, 3) * que seria el 3 de junio de 2016. El 116 es es agno pues * en java se comienza con el agno 1900; asi, el agno 116 * corresponde en realidad al agno 1900 + 116 = 2016. */ this.fechaRegistro = new Date(); this.vip = vip; }
// Getters y Setters public int getIdCliente() { return this.idCliente; } public Date getFechaRegistro() { return this.fechaRegistro; } public boolean isVip() { return this.vip; } public void setVip(boolean vip) { this.vip = vip; }
// Metodo toString public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Cliente{"); sb.append("idCliente=").append(this.idCliente); sb.append(", fechaRegistro=").append(this.fechaRegistro); sb.append(", vip=").append(this.vip); sb.append(", Persona{").append(super.toString()); sb.append("}}"); return sb.toString(); }}y finalmente realizamos pruebas
xxxxxxxxxxpackage Herencia;
public class Simulacion1 { public static void main(String[] args) {
/* Crearemos varios objetos de la clase Persona * Como tenemos tres constructores, podemos utilizar * cualquiera de ellos, veamos un ejemplo de cada uno */ // Objeto con un constructor vacio Persona persona1 = new Persona(); /* Objeto con el constructor con un parametro * donde el parametro es el nombre */ Persona persona2 = new Persona("Lola"); // Constructor con todos los parametros Persona persona3 = new Persona("Amlo", 'M', 20, "Calle falsa 123"); // Invocamos el metodo toString System.out.println("Del constructor vacio " + persona1); System.out.println("Del constructor con un parametro " + persona2); System.out.println("Del constructor con todos los parametros " + persona3); System.out.println("\n-------------\n");
/* Ahora, crearemos un objeto de la clase * empleado, que hereda de la clase persona */ Empleado empleado1 = new Empleado("Luis", 'M', 25, "Calle falsa 31416", 10); /*Gracias al construtor de la clase empleado ( el cual *sobreescribe el constructor de la clase padre utilizando * super() ) podemos acceder a los atributos de la clase *padre: nombre, genero, edad, direccion */ System.out.println(empleado1.nombre); System.out.println(empleado1.genero); System.out.println(empleado1.edad); System.out.println(empleado1.direccion);
// Luego, podmeos acceder a los atributos propios de la clase hija System.out.println("\nAtributos propios de la clase hija"); System.out.println("Id empleado: " + empleado1.getIdEmpleado()); System.out.println("Sueldo: " + empleado1.getSueldo());
/* De forma general, podemos ver toda la informacion * de nuestro objeto utilizando el metodo toString * de la clase empleado */ System.out.println(empleado1); }}xxxxxxxxxxDel constructor vacio Persona{nombre='null', genero=, edad=0, direccion='null'}Del constructor con un parametro Persona{nombre='Lola', genero=, edad=0, direccion='null'}Del constructor con todos los parametros Persona{nombre='Amlo', genero=M, edad=20, direccion='Calle falsa 123'}-------------LuisM25Calle falsa 31416Atributos propios de la clase hijaId empleado: 1Sueldo: 10.0Empleado{idEmpleado=1, sueldo=10.0, Persona{Persona{nombre='Luis', genero=M, edad=25, direccion='Calle falsa 31416'}}}
Más pruebas:
package Herencia;
public class Simulacion2 { public static void main(String[] args) { // Crearemos un objeto de la clase cliente Cliente cliente = new Cliente("Dolores", 'F', 96, "Calle falsa 123", true); // Imprimiremos los datos del cliente System.out.println(cliente);
// Podemos acceder al valor contadorCliente de la clase cliente System.out.println("El valor del contador es: " + Cliente.getContadorCliente()); // Creamos otro objeto Cliente cliente2 = new Cliente("Jonas", 'M', 20, "Calle falsa 123", false); // Vemos de nuevo el valor del contadorCliente System.out.println("El valor del contador es: " + Cliente.getContadorCliente()); }}xxxxxxxxxxCliente{idCliente=1, fechaRegistro=Fri Dec 09 10:21:58 CST 2022, vip=true, Persona{Persona{nombre='Dolores', genero=F, edad=96, direccion='Calle falsa 123'}}}El valor del contador es: 1El valor del contador es: 2
Crearemos una carpeta nueva denominada Paquetes. Dentro de esa carpeta crearemos dos carpetas: la primera se llamará Paquete1 y dentro de dicho paquete crearemos un nuevo archivo de java denominado Utileria.java
x
package Paquetes.Paquete1;
public class Utileria { public static void Imprimir(String s) { System.out.println("s = " + s); } }Luego, dentro de la carpeta Paquetes creamos la segunda carpeta denominada Prueba y dentro de ella creamos el archivo java denominado TestUtileria.java:
package Paquetes.Prueba;
// Importamos la clase Utileria de Paquetes.Paquete1import Paquetes.Paquete1.Utileria;
// Alternativa// import static Paquetes.Paquete1.Utileria.Imprimir;
public class TestUteleria { // Utilizaremos el método Imprimir de la clase Utileria public static void main(String[] args) { Utileria.Imprimir("Hola Mundo");
// Alternativa // Imprimir("Hola Mundo");
// Sintaxis nombre completamente calificado (no es recomendable) // Paquetes.Paquete1.Utileria.Imprimir("Hola Mundo"); }}xxxxxxxxxxs = Hola Mundo
donde desde otra carpeta estamos utilizando el método Imprimir() que definimos en la carpeta Paquete1.