Introducción a Java — Parte I

Variables y memoria

Alan Badillo Salas
Full Stack Java
6 min readSep 2, 2018

--

Java es un lenguaje de programación portable a través de diversos Sistemas Operativos y Dispositivos, dado que su código se compila para una máquina virtual llamada JVM (Java Virtual Machine). Java es un lenguaje de programación Orientado a Objetos, lo cual nos permite programar sistemas complejos a tráves de clases y objetos. Además Java tiene un gran número de desarrolladores y librerías, por lo que puede ser utilizado para diversos fines, como crear aplicaciones de escritorio, servicios web, programas de administración y mucho más.

En esta serie de entregas analizaremos a fondo las partes más importantes de Java para tener un panorama general del lenguaje y poder ampliar los conocimientos de forma autodidacta.

Puntos de entrada o Clases Ejecutables

En Java la metodología de trabajo es a través de Clases o Interfaces, por lo que para definir un archivo, deberemos llamarlo igual que el nombre de su Clase o Interfaz definida, por ejemplo, si queremos crear un programa ejecutable que muestre el mensaje hola mundo en la terminal, entonces, deberemos crear una clase llamada HolaMundo o con algún nombre que se desee y está deberá estar contenida dentro del archivo llamado exactamente igual que la clase más la extensión .java, es decir, el archivo se deberá llamar HolaMundo.java.

Para poder ejecutar el archivo, este deberá contener una clase y dentro de la clase deberemos definir un Punto de Entrada, el cual se refiere a un método especial llamado public static void main(String[] args), exactamente así. Dicho método especial es estático, esto significa que el método no tiene acceso a las propiedades definidas en la clase, es decir, este método es independiente de la clase y sólo podrá acceder a otras propiedades o métodos estáticos. El método main también es especial porque es ejecutado automáticamente por Java cuando se manda a llamar en modo ejecución, en resumen tenemos:

Una Clase de Java puede ser ejecutada desde una terminal, si y sólo si se ha definido un método especial public static void main(String[] args). Dicho método main será ejecutado y tódo su código contenido será evaluado.

Veamos entonces, un ejemplo de la clase llamada HolaMundo, contenida en el archivo de texto plano HolaMundo.java, que despliega el mensaje Hola Java en la terminal y finaliza.

Como podemos observar en el código anterior, hemos definido una clase llamada HolaMundo, la cual posee un Punto de Entrada (un método especial public static void main(String[] args)). Dentro del método main podemos observar la sentencia System.out.println("Hola Java"), la cual imprime en la salida estándar System.out el texto Hola Java, dicha salida estándar es generalmente la terminal.

Variables y Memoria

Las variables nos permiten acceder a los datos almacenados en la memoria y existen dos tipo de variables, las primitivas, que son almacenadas en la Memoria Stack y las variables complejas, que son almacenadas en la Memoria Heap.

Las variables primitivas pueden almacenar datos simples, por ejemplo, números enteros o decimales, caracteres y bytes. Los tipos de datos asociados a las variables primitivas son int, float, double, char, byte, entre otros. Las variables primitivas retienen valores absolutos mediante la Memoria Stack, la cuál se encarga de reservar memoria para cada tipo de dato según los bytes que requiera, por ejemplo, para un entero descrito en 32 bits, debería reservar 4 bytes (1 byte equivale a 8 bits). En la memoria stack, cada valor accedido genera una copia, por lo que asignar una variable primitiva a otra equivale a crear una nueva variable con una copia del valor de la otra variable, a esto se le llama copia por valor. Puedes consultar la documentación para más detalles: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Las variables complejas son creadas a partir de instanciar una clase en un objeto. Una clase es una construcción de atributos y métodos como veremos adelante. Cuando una clase es instanciada, se refiere a que el programa solicita un nuevo espacio en memoria para almacenar todos los atributos de la clase, dicho espacio será único para cada instancia, por ejemplo, si la clase require almacenar dos números enteros, entonces cada instancia solicitará la memoria necesaria para almacenar dos números enteros (8 bytes). Entonces cada instancia reserva su propia memoria, pero dicha memoria reservada es compleja, dado que en cualquier momento podrían generarse nuevas instancias y el programa debe asignar rápidamente memoria de tamaño variable, por lo que se crea el concepto de Memoria Heap, la cual consiste en una región extensa de memoria, la cual está asignado la memoria para cada instancia y de tamaño variable, de este modo, cada que una instancia solicita memoria, se busca una región disponible en el heap y se le asigna. La desventaja es que hay un tiempo de asignación que al cabo de un rato afecta el rendimiento del programa, por lo que Java implementa un recolector de basura encargado de liberar la memoria para las instancias que ya no tienen referencia (una referencia a la instancia hace que la memoria siga viva). Cuando copiamos una variable compleja (desde ahora instancia) a otra variable compleja, no se crea una copia de la memoria, sino que simplemente ambas variables se refieren a la misma memoria y lo que le pase a una variable le pasa a la otra, esto se conoce como copia por referencia. Para ver más de este tema puedes ver el siguiente video: https://youtu.be/_y7k_0edvuY

Una convensión de los programadores Java es nombrar con mayúsculas las clases, y cada clase genera un tipo de dato personalizado, por lo que si te encuentras una variable cuyo tipo de dato comience por mayúscula, automáticamente debes saber que se trata de un tipo de dato complejo y las variables serán complejas, por ejemplo, si creamos la variable String mensaje; o Persona persona.

Vamos a ver un ejemplo de un programa que utiliza variables primitivas para calcular el siguiente número entero.

Ahora vamos a comparlo con un programa que utiliza variables complejas y sus implicaciones.

Podemos ver que cuando utilizamos variables primitivas, estas crearán copias de sus valores cuando sean enviadas a otros métodos o asignadas a otras variables. Sin embargo, cuándo utilicemos variables complejas (variables de clase), estarémos copiando la referencia a la memoria que le fue asignada a la instancia referente, por lo que si pasamos la variable a tráves de un método o la asignamos a otra variable, en realidad estaremos enviando la dirección de memoria del heap dónde se encuantran los datos del objeto, por lo que todas las variables referenciadas podrían ser modificadas. Para más detalles puedes visitar: https://www.geeksforgeeks.org/variables-in-java/

Las variables primitivas son puertas de acceso a los datos almacenados en la memoria stack, por convención iniciarán con minúscula como int, float, double, etc. Cuándo copiamos una variable primitiva en otra, se creará una copia de su valor en memoria. Las variables complejas igualmente son accesos a instancias almacenadas en la memoria heap, las cuales fueron previamente asignadas por el operador new y por convención comienzan con mayúsculas, cada clase genera un tipo de dato complejo, por ejemplo, String, Integer, Float, Double, Persona, etc. Cuándo copiamos una variable compleja en otra, se copiará la referncia a la memoria dónde se almacena la instancia, por lo que ambos objetos estarán trabajando sobre la misma instancia y se afectarán entre sí.

Con esto concluimos está primer entrega que nos permite ver que la programación en Java parte de clases, dichas clases al poseer un método especial se pueden ejecutar. También hemos podido entender el concepto de variables y ahora somos capaces de almacenar datos en nuestro programa, tanto datos primitivos como complejos.

Ahora deberías poder resolver los siguientes ejercicios sin problemas.

Problema 1. Crear una clase llamada Suma la cuál contenga un punto de entrada main, crear dos variables de tipo int llamadas a y b inicializada con dos números que se desee, imprimir la suma de a y b.

Problema 2. Crear un programa (una clase con punto de entrada) que defina una variable de tipo String inicializada en Hola mundo de Java e imprimir la longitud de la variable utilizando el método length().

Problema 3. Investiga cómo funciona la clase Scanner del paquete java.util para leer un número entero desde el teclado. Hint: utiliza Scanner sc = new Scanner(System.in) y sc.nextInt(). Agrega las excepciones necesarias.

Problema 4. Utiliza la clase Scanner para leer una cadena de texto.

Problema 5. Crea un programa que lea dos números enteros desde el teclado e imprima la suma de ellos. Nota: imprime antes un mensaje para que el usuario sepa que se están esperando los números.

--

--

Alan Badillo Salas
Full Stack Java

L. Matemáticas Aplicadas UAM-Cuajimalpa. M. en Inteligencia Artificial IPN-CIDETEC. Desarrollador Full Stack MEAN/MERN. Data Scientist.