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:
xxxxxxxxxx
package 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;
}
}
xxxxxxxxxx
x = 10
arg1 = 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 persona
La cual se encontrará almacenada en una carpeta denominada clases
. Luego, consideremos
xxxxxxxxxx
package PasoPorValor;
// Importamos la clase Persona de la clase clases
import 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
*/
}
xxxxxxxxxx
Juan Perez
Karla 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.
xxxxxxxxxx
package 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;
}
}
xxxxxxxxxx
p1: 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
xxxxxxxxxx
package 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
xxxxxxxxxx
package 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();
}
}
xxxxxxxxxx
package Herencia;
// Importamos la clase necesaria para trabajar con fechas
import 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
xxxxxxxxxx
package 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);
}
}
xxxxxxxxxx
Del 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'}
-------------
Luis
M
25
Calle falsa 31416
Atributos propios de la clase hija
Id empleado: 1
Sueldo: 10.0
Empleado{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());
}
}
xxxxxxxxxx
Cliente{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: 1
El 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.Paquete1
import 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");
}
}
xxxxxxxxxx
s = Hola Mundo
donde desde otra carpeta estamos utilizando el método Imprimir()
que definimos en la carpeta Paquete1.