Saltearse al contenido

Programación Orientada a Objetos

Como mencionamos previamente, las variables pueden tener distintos tipos de datos. Así mismo, Java establece una clara diferencia entre estos tipos de datos, y los agrupa en dos grandes categorías: tipos de datos primitivos y tipos de datos no primitivos (o clases). Si bien hemos analizado esta primer categoría, necesitaremos conocer algunos conceptos fundamentales antes de poder comprender los tipos de datos no primitivos.

Hasta el momento, hemos visto que la programación es una forma de resolver problemas, y para ello hemos utilizado un paradigma de programación llamado imperativo. En este paradigma, el programa se compone de una serie de instrucciones que se ejecutan secuencialmente, y que modifican el estado del programa.

No obstante, el enfoque imperativo tiene sus limitaciones. Razón por la cual han surgido nuevas maneras de interpretar y representar los sistemas que buscamos implementar. Una de ellas es el paradigma orientado a objetos.

El Paradigma Orientado a Objetos, o bien la Programación Orientada a Objetos (POO), es una forma de programar que tiene como objetivo expresar los elementos de la vida real como unidades esenciales del software que desarrollamos. Así como también, las relaciones que existen entre ellos.

Será vital que comprendamos cómo funciona este paradigma, ya que Java está basado fundamentalmente sobre él. Por este motivo, aprenderemos los conceptos básicos de la Programación Orientada a Objetos, y cómo aplicarlos.

Clases y Objetos

Para comenzar, debemos entender que POO se basa en los conceptos de clases y objetos. Estos elementos permitirán estructurar un programa en piezas simples y reutilizables.

Dentro del contexto POO, un objeto es un componente que tiene un rol específico y puede interactuar con otros. En este sentido, se trata de establecer una relación entre un objeto del mundo real y un componente de software.

Por otro lado, una clase puede entenderse como una plantilla que define las características y el comportamiento de un objeto. Es decir, una clase es un modelo que define las propiedades y el comportamiento de un objeto.

Es importante distinguir estos dos conceptos que, a menudo, se confunden. Para ello, introduciremos un nuevo concepto: la instanciación.

De esta forma, podemos tener diferentes objetos que pertenezcan a una misma clase. Podemos usar como sinónimos las palabras ‘objeto’ e ‘instancia’ de una clase.

En retrospectiva, recordando el paradigma imperativo, podemos asociar una clase con un tipo de dato. Es decir, una clase es un tipo de dato no primitivo. De igual manera, podemos asociar un objeto con una variable. Es decir, un objeto es una variable cuyo tipo de dato es una clase.

Por ejemplo, si buscamos representar un auto, podemos definir una clase Auto que tenga las propiedades de este objeto en el mundo real. Posteriormente, veremos cómo definir una clase en Java. Por ahora, supongamos que ya tenemos una clase Auto definida.

Entonces, empleando esta clase podemos crear un objeto carro que represente un auto en particular. En primer lugar, declararemos el objeto carro, tal y como lo haríamos con una variable de tipo primitivo:

1
Auto carro;

En este punto podemos afirmar que el objeto carro existe, pero aun no se encuentra instanciado y, en términos de POO, el objeto no “está vivo”.

Para instanciar un objeto en Java, debemos utilizar la palabra reservada new seguida del nombre de la clase que queremos instanciar.

1
Auto carro = new Auto();

De esta forma, el objeto carro se encuentra instanciado y, en términos de POO, el objeto “está vivo”. Aunque pueda parecer un poco extraño referirse a un objeto de esta manera, es importante entender que sólo los objetos “vivos” pueden interactuar con otros objetos.

Sin realizar el proceso de instanciación, un objeto no es más que una variable que ocupa un espacio en memoria del cual no podemos aprovechar nada. Por lo tanto, la instanciación es un paso fundamental para poder utilizar un objeto.

Recordando cuando hablamos del tipo de dato String, hemos mencionado que es un tipo de dato no primitivo. Es decir, String es una clase que representa una cadena de caracteres.

Hasta el momento, solo hemos visto cómo instanciar cadenas de caracteres asignándolas a literales.

1
String cadena = "Hola Mundo";

Sin embargo, como String es una clase, si lo deseamos podemos instanciar un objeto de esta clase utilizando el operador new. En particular, veremos que podemos indicar durante la instanciación la cadena de caracteres que queremos representar.

1
String cadena = new String("Hola Mundo");

Estas sentencias son equivalentes y, en ambos casos, estamos instanciando un objeto de la clase String. No obstante, la primera forma es la más utilizada, ya que es más sencilla y legible, razón por la cual la hemos utilizado hasta el momento.

Así también, si prestamos atención a los ejemplos que hemos visto hasta el momento, podemos notar que hemos instanciado objetos de varias clases. Por ejemplo, Scanner para leer datos desde la consola, Array para representar un arreglo, etc. Ante todos estos casos, hemos de entender que trabajar con objetos es imprescindible en Java.

Atributos y Métodos

Ahora que hemos contextualizado como funciona el paradigma orientado a objetos, debemos mencionar como es que logramos representar un objeto como un componente de software. La manera de lograr esto es muy simple, mediante las clases.

Para esto las clases cuentan con dos elementos fundamentales, que son los atributos y los métodos.

Los atributos representan las características que definen o identifican a un objeto. Por ejemplo, si queremos representar un auto, podemos definir los atributos marca, modelo, color, año, placa, cilindraje, transmisión, tracción, combustible, kilometraje, precio, estado, etc.

Por otro lado, los métodos representan el comportamiento de un objeto. Es decir, las acciones o funcionalidades que puede realizar un objeto. Por ejemplo, si queremos representar un auto, podemos definir los métodos encender, apagar, acelerar, frenar, cambiarMarcha, cambiarTracción, cambiarCombustible, cambiarEstado, etc.

Tomando como ejemplo la clase String, con la que venimos trabajando, podemos apreciar que cuenta con atributos y métodos. Por ejemplo, el atributo length nos permite conocer la longitud de una cadena de caracteres. Por otro lado, el método toUpperCase nos permite convertir una cadena de caracteres a mayúsculas.

1
String cadena = "Hola Mundo";
2
3
System.out.println(cadena.length()); // 10
4
System.out.println(cadena.toUpperCase()); // HOLA MUNDO

length es un atributo de la clase String, el cual, para esta instancia en particular, tiene un valor de 10.

Por otro lado, el método toUpperCase es un comportamiento de la clase String, el cual, para esta instancia en particular, devuelve una nueva cadena de caracteres con todos sus caracteres en mayúsculas ("HOLA MUNDO").

Referencias

Cuando describimos la manera en que funcionan las variables, mencionamos que estas almacenan valores en memoria. Sin embargo, dependiendo del tipo de dato de la variable, esta definición puede variar.

Por un lado, las variables de tipo primitivo como int, float, double, boolean, char, etc. almacenan directamente los valores que se les asignan. Por ejemplo, si declaramos una variable de tipo int y le asignamos el valor 10, la variable almacenará directamente el valor 10 en el espacio de memoria que ocupa.

Sin embargo, esto no ocurre cuando las variables pertenecen a una clase, donde los valores que se almacenan en las variables son referencias a los objetos.

Es decir, las variables no almacenan directamente los valores de los objetos, sino que almacenan una referencia a la ubicación en memoria donde se encuentra el objeto.

Por ejemplo, si declaramos una variable de tipo String y le asignamos el valor "Hola Mundo", la variable no almacenará directamente el valor "Hola Mundo" en el espacio de memoria que ocupa. De igual manera, si declaramos una variable de tipo Array, ArrayList, HashMap, Auto, etc. y le asignamos un valor, la variable no almacenará directamente el valor que le asignamos.

Esto, puede ser difícil de comprender, pero es importante que lo entendamos, ya que podría llevarnos a cometar errores al momento de programar.

Para entender mejor esto, expandiremos los conceptos hablando sobre el “stack” y el “heap” en el siguiente apartado.