Lenguajes de Programación PDF

Document Details

ImpressiveAntigorite7807

Uploaded by ImpressiveAntigorite7807

Universidad Siglo 21

Tags

lenguajes de programación programación computadoras tecnológica

Summary

Este documento presenta una introducción a los lenguajes de programación, incluyendo el histórico enfrentamiento Deep Blue-Kasparov como ejemplo. Explora diferentes tipos de lenguajes de programación y algoritmos utilizados en computación, incluyendo el lenguaje ensamblador y lenguajes de alto nivel. Examina el progreso tecnológico y su relación con el desarrollo del software.

Full Transcript

Lenguajes de programación Introducción Actualmente la computadora es una herramienta fundamental para realizar tareas de diversa complejidad en la sociedad moderna. Su aplicación permitió una expansión tecnológica sin precedentes, con impacto directo en la forma de vida de las personas. En esta opo...

Lenguajes de programación Introducción Actualmente la computadora es una herramienta fundamental para realizar tareas de diversa complejidad en la sociedad moderna. Su aplicación permitió una expansión tecnológica sin precedentes, con impacto directo en la forma de vida de las personas. En esta oportunidad profundizaremos en los lenguajes de programación que nos permiten generar el código con el cual controlaremos la computadora. 1. Caso: el enfrentamiento Deep Blue-Kasparov Ese caso tiene sus orígenes en 1985 en la Universidad Carnegie Mellon (Pensilvania, Estados Unidos) cuando se comenzó a trabajar en un proyecto denominado ChipTest, una máquina para jugar al ajedrez. “Justo en este momento (…) Garry Kasparov se convertía, con tan solo 22 años en el Campeón Mundial de Ajedrez más joven de la historia” (Adaime, 2011, p. 22), al vencer a otro gran maestro ruso, Anatoly Karpov. “ChipTest podía analizar 50.000 movimientos por segundo” (Adaime, 2011, p. 22). Muchísimo, comparado con Kasparov, pero muy poco si lo comparamos con los 200 millones de jugadas que puede analizar Deap Blue en el mismo tiempo. Unos años después, ChipTest “tuvo una evolución en Deep Thought, más potente y eficaz” (Espeso, 2014, https://www.xataka.com/otros/deep-blue-el-ordenador-con-una-sola-mision-ganar- al-humano), pero todavía sin posibilidades de ganar a los grandes jugadores de ajedrez del mundo. El mismo Kasparov compitió dos veces, y ganó ambas. IBM contrató al equipo que desarrolló estos dos sistemas en 1989, con el objetivo de dar continuidad, tecnología y financiamiento al proyecto Así nació Deep Blue, nombre en honor a big blue, que es el nombre con el que se conoce popularmente a la empresa. El primer torneo entre Kasparov y Deep Blue tuvo lugar en Filadelfia, Pensilvania, en febrero de 1996. A pesar de que el computador era capaz de calcular 100 millones de posiciones por segundo y el campeón ruso solo podía analizar 2 o 3 en el mismo tiempo, el resultado final fue a favor de Kasparov quien ganó 3 partidas, empató 2 y perdió una. Un año después, vino la parte más conocida de esta historia, con una máquina mejorada y el doble de rápida que su antecesora. En mayo de 1997 tuvo lugar el segundo torneo con un premio consistente en 1,1 millones de dólares, que se dividía en 700 mil para el ganador y el resto para el perdedor. Deep Blue podía analizar entonces 200 millones de posiciones por segundo. “Los creadores de la máquina de IBM afirmaron que una movida que a Deep Blue hace en 2 o 3 minutos, una Pentium Pro —una PC hogareña— lo realiza en 6 o 7 horas” (Adaime, 2011, p. 19). El software, desarrollado en C, implementaba algoritmos de fuerza bruta para calcular todas las posibilidades de movimientos y su probabilidad al comparar con su base de más de 700.000 de mejores partidas de ajedrez del mundo. “De ahí, la gran capacidad de procesamiento en paralelo necesario para evaluar las jugadas” (Velasco, 2011, https://hipertextual.com/2011/11/inteligencia-artificial-deep-blue- kasparov). En este torneo, luego de las 5 primeras partidas estaban empatados 1-1 con tres tablas. Sin embargo, Kasparov fue derrotado en la sexta y última partida, porque cometió un error al inicio que posiblemente haya estado asociado al cansancio. Presentación del caso Como programador, te convocan de IBM para introducir mejoras en el software de Deep Blue, para aprovechar las ventajas tecnológicas que surgieron en los últimos 25 años. Para llevar adelante esta tarea, revisaremos juntos algunos conceptos importantes. Partimos de la base de que, históricamente, el hombre intentó resolver problemas, agilizar el trabajo y simplificar cálculos. En la actualidad, esta situación tomó mayor importancia, porque es necesario interpretar una gran cantidad de información en períodos de tiempo muy cortos para tomar decisiones. Esto fue posible gracias al progreso tecnológico que permitió mejorar las técnicas de producción, cambiar las formas de consumo y generar nuevas maneras de entretenimiento. Desde la invención del ábaco, las calculadoras mecánicas, hasta la explosión de las computadoras electrónicas siempre se buscó desarrollar tecnologías cada vez más rápidas y precisas. Inicialmente, “las computadoras electrónicas se programaban directamente usando el conjunto de instrucciones del procesador, en código máquina” (Gallardo, Pomares, Botía y Martínez, 2021, https://acortar.link/cb8Euk). Esta tarea no es sencilla, entonces surge el lenguaje ensamblador con un primer traductor y posteriormente aparecen los lenguajes de programación de alto nivel. Las demandas de software crecen de manera sostenida en este último tiempo, por lo que generar un programa de forma rápida y minimizar potenciales fallas es todo un desafío. Los programas orientados a objetos son más fáciles de comprender, modificar y reutilizar que los estructurados. 2. Progreso tecnológico: hardware y software Ya comentamos sobre la búsqueda constante por resolver problemas, agilizar el trabajo y simplificar cálculos. Sin dudas, un factor disruptivo en este proceso fue la aparición de la computadora y su evolución a lo largo del tiempo, para convertirse en una herramienta de procesamiento con suficiente poder de cálculo para realizar tareas de forma muy eficiente. Una computadora es un dispositivo electrónico diseñado para aceptar datos de entrada, almacenarlos, realizar operaciones con ellos y entregar resultados en su salida. El procesamiento de estos datos se realiza mediante la ejecución de un programa, que representa una secuencia ordenada de instrucciones entregadas a la computadora y con una lógica predeterminada por uno o más algoritmos. En este punto, identificamos que es necesario un lenguaje de programación en el que se escribe el código con las instrucciones que dan origen a la generación del programa. Es importante tener presente que, tanto los algoritmos como los programas, hacen referencia a un conjunto de instrucciones, pero los primeros pueden estar escritos en lenguaje coloquial o de máquina y ser ejecutados por personas, mientras que los programas deben estar escritos en lenguaje de máquina y ser ejecutados por las computadoras. Es decir, que un programa es la representación de un algoritmo en lenguaje de programación. “Cualquier tecnología suficientemente avanzada es indistinguible de la magia” (Arthur C. Clarke, en Parra, 2013, https://acortar.link/Jp81d1). El progreso tecnológico está asociado al desarrollo del hardware y software, que son los elementos indispensables en cualquier arquitectura en computadora. El hardware es el conjunto de dispositivos electrónicos que componen la parte física y el software es el conjunto de programas para procesar los datos. La evolución del hardware desde los primeros prototipos a los equipos que hoy existen en el mercado está dividida en generaciones, donde cada una supone un cambio tecnológico muy notable. Al principio el software se consideraba complementario. Existían pocos métodos de programación, prácticamente no había documentación y el desarrollo se realizaba a medida de cada necesidad. Cuando la complejidad de los sistemas incrementó, se hizo necesario migrar de un tipo de programación con instrucciones simples para las computadoras a otro con palabras más parecidas al lenguaje humano con el objetivo de simplificar su escritura y que luego sean ejecutadas por una computadora. Existen distintos tipos de programas que permiten relacionar el hardware con el software, los cuales podemos dividir en tres grandes grupos, a saber. Sistema operativo: es el software que coordina todos los servicios y aplicaciones que se utilizan en una computadora. Provee además los drivers necesarios para que se pueda realizar el manejo del hardware en los mismos. “Software de programación: es el conjunto de herramientas que permiten (…) desarrollar programas (…) usando diferentes alternativas [de metodología] y lenguajes” (Maida y Pacienzia, 2015, p. 15), Software de aplicación: son los programas que están escritos en algún lenguaje de programación para realizar actividades que resulten de utilidad para los usuarios. 3. Lenguajes de programación En nuestra vida diaria nos comunicamos con otras personas mediante el uso de un lenguaje que puede ser oral o escrito. Los lenguajes de programación nos permiten la realización de un código, a partir del cual se generan las instrucciones que se ejecutan en la computadora. Están formados por un conjunto de palabras reservadas, símbolos y reglas que definen su estructura. ¿Cuáles son los lenguajes que recomendarías utilizar para llevar adelante las tareas mencionadas en el caso? En 1950, Claude Shannon distinguió dos tipos de posibilidades sobre algoritmos de juegos en computadoras, los programas tipo A y tipo B. Los primeros basados en fuerza bruta, es decir, trabajan un árbol de posibilidades y exploran todas las ramas del árbol hasta llegar a la mejor solución. Los segundos analizan solamente las mejores jugadas de cada posición, para lo cual se requiere el aprendizaje de lo que hoy conocemos como ‘inteligencia artificial’ y para lo cual es recomendable el uso de programas orientado a objetos. En el caso planteado mencionamos que se utilizaba lenguaje C, y ahora proponemos programación orientada a objetos. En ambos casos mencionamos lenguajes de alto nivel. Repasaremos juntos cuáles son los distintos tipos y sus características. La división de los lenguajes de programación se realiza de acuerdo con su proximidad al lenguaje que entiende la computadora y su facilidad para ser comprendido por el programador. Lenguaje de máquina Es el lenguaje de más bajo nivel. Cualquier computadora puede entender de manera directa solo su propio lenguaje máquina, el cual se define a partir de su diseño de hardware, y finalmente consta de números binarios (0 y 1) que dan instrucciones. Los métodos de programación en este lenguaje ocupan mucho tiempo además de ser poco prácticos y difíciles de comprender para los programadores. Lenguajes ensambladores Los lenguajes ensambladores representaron un gran avance en la programación, ya que en vez de cadenas de números se utilizan abreviaturas para representar las operaciones elementales. Claro que fue necesario desarrollar traductores (que se llamaron ensambladores) para convertir los programas en lenguaje ensamblador a lenguaje máquina, a la velocidad de la computadora. Lenguajes de alto nivel Los lenguajes de alto nivel transformaron la forma de programar. En la elaboración del código se utilizan palabras que son más simples de comprender por los humanos y cubren más instrucciones que su predecesor. Esto permite a los programadores menos esfuerzo para lograr los resultados y mayor simplicidad para aprenderlos. Se requiere de un compilador, que realiza la traducción del programa fuente escrito en un lenguaje de alto nivel a un código objeto con instrucciones de máquina, que son las que luego se ejecutan. Existen compiladores para cada lenguaje y cada máquina, pero una ventaja de estos lenguajes es que se pueden trasladar de una máquina a otra con poco esfuerzo. Los intérpretes analizan y ejecutan cada instrucción del programa escrito en alto nivel y lo convierten en instrucciones de máquina, sin guardar el código objeto. Cuando se corre un programa interpretado se evita el retraso de la compilación, pero son más lentos al tener que convertir cada línea del código en lenguaje de maquina a medida que se ejecuta. Son más flexibles a los cambios en entornos de programación, porque un mismo archivo fuente puede levantarse en distintos hardware. Al compilarlo se genera un programa específico para el hardware que se compiló. Existen también enfoques mixtos, como el usado por Java, en el que se realizan ambos procesos. En una primera fase el compilador de Java (javac) realiza una traducción del código fuente original a un código intermedio binario independiente (…) (bytecode). Este código binario es multiplataforma, luego es procesado por el intérprete (java) que sí es dependiente de la plataforma. (Gallardo, Pomares, Botía y Martínez, 2021, https://acortar.link/cb8Euk) Los lenguajes de alto nivel proporcionan algunos beneficios, a saber. Evitan tener que conocer detalles de máquina de bajo nivel, como el direccionamiento de memoria y conocer el set de instrucciones de un procesador específico. Se pueden ejecutar en diferentes marcas de computadora. Son más fáciles de usar que los lenguajes ensambladores y de aprender para los programadores. A continuación, se muestra el ejemplo de una instrucción simple que utiliza un lenguaje de alto nivel, el lenguaje ensamblador y el código de máquina. Código de Lenguaje Lenguaje alto Dirección máquina ensamblador nivel 00010101 10000001 LOAD A 00010111 10000010 ADD B 00010110 10000011 STORE C C=A+B Te invito a ver la línea temporal de los lenguajes de programación de alto nivel. Fuente: Grupo C IS513 (s.f.). Línea de tiempo de los lenguajes de programación. Recuperado de https://acortar.link/DcuYv3. Programación estructurada El término crisis del software surge en 1968 para describir los frecuentes problemas que aparecían durante el proceso de desarrollo de nuevo software, que no se terminaban a tiempo y provocaba un incremento en los costos. “Los programadores utilizaban en los lenguajes (…) el comando GOTO para realizar una bifurcación condicional” (Sy Corvo, 2020, https://www.lifeder.com/programacion-estructurada/), lo que generaba dificultad en la lectura y algunos problemas de contexto lógico. Como una respuesta a este planteamiento nació el método de programación estructurada. La programación estructurada es aquella en la que el código cuenta con una estructura modular que sigue una secuencia lógica para que su función sea la correcta. Como regla general, las instrucciones se ejecutan sucesivamente una tras otra, lo que permite que el programa sea leído de arriba hacia abajo sin perder la continuidad. En diversas partes del programa las mismas pueden ejecutarse o no según las condiciones. Una de las características importantes de este paradigma es la modularidad, lo que permite mayor simplicidad en la depuración de errores y flexibilidad para la introducción de mejoras. Cada módulo es un procedimiento o función que tiene una entrada y una salida. A este tipo de paradigma donde la programación estructurada se divide en módulos, se lo identifica también como programación modular. Las estructuras de control que se utilizan son, a saber. Secuencial: las instrucciones se ejecutan, sucesivamente, una detrás de otra. Bifurcación: permite la selección entre dos estructuras secuenciales de acuerdo con el resultado de evaluación (IF-THEN-ELSE). Repetitiva o Iteración: se realiza la ejecución de una estructura secuencial mientras se cumpla el bucle condicional (WHILE o sus variantes). No debe utilizar la instrucción GOTO. El término programación estructurada ahora se relaciona no sólo con la lógica de control del programa, sino con cómo se diseñan y desarrollan los programas. Actualmente las personas que hablan de programación estructurada, a menudo se refieren a una forma sistemática de analizar los problemas informáticos y diseñar soluciones llamadas de arriba a abajo. En la programación de arriba abajo, el problema se descompone en una serie de problemas más pequeños, y a cada uno de ellos se los resuelve. Una vez que se resuelven los problemas más pequeños, las soluciones se combinan para resolver el problema más grande. (Patterson Hume y Stephenson, 2000, p. 9) En la tabla 1 se resumen algunas ventajas y desventajas de la programación estructurada. Tabla 1: Ventajas y desventajas de la programación estructurada Ventajas Desventajas Los programas son fáciles de entender, Alta vinculación entre datos y código. pueden leerse en forma secuencial. Es más simple hacer pruebas y En programas grandes aumenta el corrección de errores debido a su código repetido. modularidad. La indentación de las instrucciones Existe mayor dificultad para el reuso de ayuda a alguien que lee un programa a código. entender su lógica más fácilmente. Cambio en variables o tipo de datos puede generar inconsistencias. Fuente: elaboración propia. Un ejemplo de lenguaje no estructurado es Assembler. Lenguajes de alto nivel como FORTRAN, COBOL y BASIC originalmente no tenían estructuras, pero luego las incorporaron. Ejemplos de lenguajes estructurados son: Pascal y C. “Los ordenadores en sí mismos, y el software aún no desarrollado, va a revolucionar la forma en que aprendemos”(Steve Jobs, en Literato, s.f., https://acortar.link/Cprcc9) Programación orientada a objetos En la llamada crisis del software aparecieron problemas de tiempos y costos en el desarrollo. Al mismo tiempo, se observaba una disminución de precios y un aumento de capacidad de procesamiento del hardware debido a la aparición de circuitos integrados. Esta situación presentó otros desafíos tales como definición de criterios de desarrollo, creación de programas más complejos, satisfacción de una demanda creciente de software y mantenimiento o modificaciones de los programas. Por ello, se necesitaba encontrar nuevas maneras de pensar, desarrollar y mantener los programas. La programación orientada a objetos es un paradigma donde se utilizan unidades lógicas llamadas objetos y sus interacciones para diseñar aplicaciones y programas. Los objetos se crean a partir de una plantilla que se denomina clase, mediante un proceso de instanciación. Tienen atributos que son los datos que le da la clase, y métodos que son las funcionalidades o acciones que le da la clase. Con este paradigma se logra que el código sea reutilizable, organizado y fácil de mantener. En programas complejos es más simple trabajar en objetos, ya que en lugar de pensar en funciones pensamos en las relaciones o interacciones entre los distintos componentes que lo componen. Con paradigma de programación nos referimos al conjunto de ideas que constituyen la base de un modo particular de programación. La programación orientada a objetos se define como un paradigma de la programación, una manera de programar específica, donde se organiza el código en unidades denominadas clases, de las cuales se crean objetos que se relacionan entre sí para conseguir los objetivos de las aplicaciones. Podemos entender la programación orientada a objetos (POO) como una forma especial de programar, más cercana a como expresaríamos las cosas en la vida real que otros tipos de programación, que permite diseñar mejor las aplicaciones, llegando a mayores cotas de complejidad, sin que el código se vuelva inmanejable. (Álvarez, 2021, https://desarrolloweb.com/articulos/499.php) Las características fundamentales de la programación orientada a objetos son: Abstracción: consiste en trabajar solo con los métodos y atributos seleccionados de los objetos, sin que estos deban dar a conocer la forma en la cual se implementan estas características. Facilita el mantenimiento de código de gran tamaño. Encapsulamiento: el objeto contiene toda la información importante “dentro del mismo y solo expone la información elegida al exterior” (Darias Pérez, 2021, https://intelequia.com/blog/post/3072/qu%C3%A9-es-la-programaci%C3%B3n-orientada-a- objetos). Los atributos internos de un objeto deberían ser inaccesibles desde fuera. Herencia: “define relaciones jerárquicas entre clases, de modo que atributos y métodos comunes puedan ser reutilizados. Las clases principales extienden atributos y comportamientos a las clases secundarias” (Darias Pérez, 2021, https://intelequia.com/blog/post/3072/qu%C3%A9-es-la-programaci%C3%B3n-orientada-a- objetos). Polimorfismo: es la capacidad que tienen los objetos de una clase de ofrecer respuesta distinta e independiente en función de los parámetros utilizados. En la tabla 2 se resumen algunas ventajas y desventajas de este tipo de lenguaje. Tabla 2: Ventajas y desventajas de la programación orientada a objetos Ventajas Desventajas Necesidad de una mayor Facilidad para reutilizar el código. documentación. El encapsulamiento permite trabajar en La ejecución de los programas puede equipo y proteger la información. ser más lenta que los estructurados. Evita la duplicación del código. La abstracción facilita la construcción de sistemas complejos y modificaciones. Fuente: elaboración propia. Algunos ejemplos de lenguajes que utilizan programación orientados a objetos son Java, Python, C++, Javascript, PHP; entre otros. Actividades de repaso 1) En consideración del modelo de programación orientada a objetos, si mencionamos el color, velocidad, cantidad de cambios de un automóvil hacemos referencia a los atributos. Verdadero Falso Justificación 2) El paradigma de la programación orientada a objetos permite __________ el código para aprovechar lo que ya tenemos programado. Usar Reutilizar Eliminar Justificación Referencias Adaime, I. (2011). Kasparov vs Deep Blue: la conflictiva relación hombre máquina. 1° edición. Buenos Aires: Universidad de Buenos Aires. Facultad de Ciencias Sociales, Argentina. Álvarez, M. A. (2021). Qué es la programación orientada a objetos. Recuperado de https://desarrolloweb.com/articulos/499.php. Darias Pérez, S. (2021). Qué es la programación orientada a objetos. Recuperado de https://intelequia.com/blog/post/3072/qu%C3%A9-es-la-programaci%C3%B3n-orientada-a- objetos. Data is beautiful (2019). Most popular programming languages 1965 – 2019 [archivo de video]. Recuperado de https://www.youtube.com/watch?v=Og847HVwRSI. Espeso, P. (2014). Deep Blue, el ordenador con una sola misión: ganar al humano. Recuperado de https://www.xataka.com/otros/deep-blue-el-ordenador-con-una-sola-mision-ganar-al-humano. Gallardo, D., Pomares, C., Botía, A. y Martínez, F. (2021). Tema 1: Historia y conceptos de los lenguajes de programación. Recuperado de https://acortar.link/cb8Euk. Grupo C IS513 (s.f.). Línea de tiempo de los lenguajes de programación. Recuperado de https://acortar.link/DcuYv3. Literato (s.f.). Steve Jobs. Recuperado de https://acortar.link/Cprcc9. Maida, E. G. y Pacienzia, J. (2015). Metodologías de desarrollo de software [tesis]. Universidad Católica Argentina, Facultad de Química e Ingeniería “Fray Rogelio Bacon”, Argentina. Parra, S. (2013). Las tres leyes de Clarke. Recuperado de https://acortar.link/Jp81d1. Patterson Hume, J. N. y Stephenson, C. (2000). Introduction to Programming in Java. Holt Software Assoc Inc. Sy Corvo, H. (2020). Programación estructurada: características, ejemplos, ventajas, aplicaciones. Recuperado de https://www.lifeder.com/programacion-estructurada/. Velasco, J. J. (2011). Inteligencia artificial: Deep Blue y Kasparov. Recuperado de https://hipertextual.com/2011/11/inteligencia-artificial-deep-blue-kasparov. Origen de Java y características principales 1. Caso: el éxito del Minecraft, cuando el éxito depende de uno Es inentendible para cualquiera que lo vea de afuera. ¿Qué posibilidades de éxito tiene un videojuego que se vende sin terminar, tiene una imagen bastante antigua, sus gráficos parecen mal elaborados y nadie gana ni pierde sólo juega? Ninguna, respondería cualquier persona que esté en el tema. Se equivoca, hay uno que lo logra y después de 10 años sigue siendo un éxito y en esta cuarentena ha logrado pasar a ser el videojuego más vendido de la historia. El Minecraft ha conseguido que lo jueguen millones de personas de todas las edades, solas o en grupo, y demuestra que la perfección no es lo importante y que su éxito está lejos de apagarse. ​(…) La historia del Minecraft es única, fue creado en 2009 por el sueco Markus Persson para Mojang (luego comprada por Microsoft) y lo hizo tomando en cuenta su pasión por los legos. Lo subió en su versión alfa (sin terminar) a la red y usó como gancho para atraer clientes que pagaran a cambio de tener la exclusividad del juego. A partir de allí fue todo un boom e incluso reparó en comentarios de los jugadores para realizar mejoras. Diez años después, donde sólo se permitía construir, ahora se le agregaron animales, grutas, barcos piratas y naves espaciales, entre otras cosas. ​ El mundo de Minecraft es básico, hay que extraer recursos y crear objetos, su potencial es ilimitado y los lugares donde uno puede “trabajar” son totalmente disímiles, desde un bosque, hasta una estación espacial, con personajes de K-Pop o con piratas, no hay límites para eso. Su éxito, por lo tanto, radica en la constante evolución, ya que las actualizaciones sirven más que nada para mejorar la experiencia de juego y ampliar la creación. Su última edición el Minecraft Dungeons (de mayo de este año) se centra en el rastreo de mazmorras llenas de monstruos para encontrar tesoros. (Celaya, 2020, https://www.laprensa.com.ar/491992-El-exito-del-Minecraft-cuando-el-exito-depende-de- uno.note.aspx) Presentación del caso Corre el año 2009. Ofrecen el desafío de participar en el equipo de desarrollo de un juego cuyo argumento es realizar tareas simples, tales como extraer recursos y crear nuevos objetos. Carlos, uno de los programadores de mayor experiencia del grupo, propone utilizar Java para realizar el juego y ahora toca el turno de dar tu opinión. ¿Qué recomendarías? Sin dudas, elegir el lenguaje de programación es un paso muy importante en la realización de un proyecto, y lo primero que debemos identificar es qué necesitamos que realice el programa. En el alcance de este juego no aparece la calidad de los gráficos como un factor crítico, sino que el factor diferencial es la experiencia de juego. Para ello, necesitamos un lenguaje que permita la flexibilidad de modificar y agregar nuevas funcionalidades, para que no se torne aburrido. En este sentido, plantear la utilización de Java como sugiere Carlos parece una muy buena opción. En el caso de Minecraft, el juego realmente se desarrolló en Java y aún hoy está vigente para todas las plataformas que soportan JVM. Con la compra de Microsoft se comenzó a trabajar en otra versión que hoy está disponible para Windows 10, consolas y dispositivos. ​Para terminar de fundamentar la recomendación revisaremos juntos algunos conceptos teóricos. 2. Origen de Java Los antecedentes de Java surgen en la década de 1980, donde C era el lenguaje por excelencia y la programación estructurada se encontraba en la cima de las preferencias. Sin embargo, para resolver complejos algoritmos se generaban grandes procedimientos, con un código complicado de mantener a largo plazo. Por este motivo, comenzó a surgir como alternativa la programación orientada a objetos, y de esta manera apareció C + +. Java comenzó a desarrollarse en Sun Microystem durante el año 1991 por James Gosling, acompañado de Eric Schmidt, John Gaga, Patrick Naughton y Wayne Rosing. Inicialmente tenía el nombre de Oak, luego cambia a Green y, finalmente, para el lanzamiento en 1995 adopta su denominación final. Un año después, en 1996, se lanza el primer Java Development kit (JDK 1.0) que proporciona el entorno para desarrollar y ejecutar el programa Java. ​“El objetivo de Java era crear un lenguaje de programación parecido a C++ en cuanto a su estructura y sintaxis, (…) orientado a objetos, pero con una máquina virtual propia” (Grupo C IS513, s.f., https://acortar.link/DcuYv3) (JVM), que lo haga independiente del microprocesador y sistema operativo. En ese tiempo, Sun Microsystems había decidido ingresar en el mercado de la electrónica de consumo y desarrollar programas para pequeños dispositivos electrónicos: microondas, tostadoras, heladeras, televisores, etcétera. James Gosling, el integrante del equipo con mayor experiencia, decidió tomar como referencia a C + +, pero intentó corregir las deficiencias que observaba. Se requería una simplicidad en el manejo de las interfaces, para que fueran mucho más intuitivas que las ventanas clásicas de la época. Adicionalmente, se necesitaba una independencia de la plataforma, ya que programas como C + + se pueden compilar para cualquier microprocesador y sistema operativo, pero para ello se requiere un compilador diferente por cada combinación, lo cual es costoso y requiere tiempo para su elaboración. Este último tema es un factor crítico en un mercado como el de electrónica de consumo donde es común la evolución de los chips por eficiencia y reducción de costos que lleva a los fabricantes a incluirlo rápidamente en sus series de producción. El surgimiento de internet le dio un impulso fundamental a Java y revolucionó la programación, cambió el modo de pensar los programas y sus funciones. Adicionalmente, la portabilidad se volvió imprescindible al considerar que internet es un universo distribuido con distintos tipos de procesadores y sistemas operativos. En 2010, Oracle adquirió Sun Microsystems y, más allá del paso del tiempo, Java se mantiene vigente. Algunos frameworks, como SpringBoot y LibGDX, tienen una estructura definida que se puede utilizar como base para desarrollar los proyectos y son muy utilizados. “Controlar la complejidad es la esencia de la programación” (Brian Kernighan, en Instituto mexicano de contabilidad, administración y finanzas, 2016, https://imecaf.com/blog/2016/05/24/la- esencia-de-la-programacion/). ¿Qué es Java? Java es un lenguaje de programación de alto nivel orientado a objetos, diseñado para el desarrollo de programas en una gran cantidad de dispositivos, independientemente del hardware y sistema operativo que se utilice. Cuenta con una amplia librería de clases y métodos que se conocen como API (interfaces de programación de aplicaciones), que creció y mejoró en las actualizaciones que aparecen periódicamente. Aunque cada programador puede crear sus clases y métodos, el uso de las API reduce los tiempos de desarrollo y, en muchos casos, mejora el rendimiento de los programas, ya que estas se encuentran optimizadas para funcionar con eficiencia. La documentación de las API es una parte importante de la documentación general de Java. Actualmente, es uno de los lenguajes con mayor adopción en el mercado y se aplica en dispositivos electrónicos, computadoras, servidores, dispositivos móviles, microservicios, juegos, internet. Se debe tener en cuenta que Java no es JavaScript: suele presentarse esta confusión debido al parecido de los nombres, pero no son el mismo lenguaje. Java es un lenguaje completo que permite realizar todo tipo de aplicaciones. JavaScript es un lenguaje de programación que permite mejorar el dinamismo de las páginas web del lado del cliente; originalmente creado por Netscape, pero actualmente es soportado por todos los navegadores y lo utilizan más del 90% de los sitios web. 3. Máquina virtual Java (JVM) Nuevamente en línea con el caso presentado, ¿qué ventaja importante podrías resaltar al considerar la selección de Java como lenguaje de programación? Haber seleccionado Java como lenguaje de programación para el desarrollo de Minecraft, posiblemente fuera uno de los factores que explican su éxito. Con la máquina virtual de Java el juego puede correr en distintas plataformas. Figura 1: Funcionamiento de la máquina virtual de Java Fuente: elaboración propia. Una máquina virtual (VM) es una aplicación de software que simula a una computadora, oculta el sistema operativo y hardware. Si se implementa la misma VM en distintas plataformas, las aplicaciones escritas para ese tipo de VM podrán funcionar independientemente del sistema operativo y hardware que tengan. En el caso de Java se la conoce como máquina virtual de Java (JVM) y es una de las más utilizadas en la actualidad. Como se observa en la figura 1, el algoritmo implementado en Java (archivo.java) se pre compila, genera un código conocido como bytecode (archivo.class). Este código no es ejecutable directamente, sino que es un input para el intérprete de Java, que es el componente de la máquina virtual de Java (JVM) que procesa las instrucciones de la aplicación. En resumen, la JVM interpreta y procesa instrucciones expresadas en bytecode. Decimos que los bytecodes de Java son portables, ya que se pueden ejecutar en cualquier plataforma que contenga una JVM con versión de Java compatible con la utilizada en el momento de compilarlos, sin necesidad de volver a compilar el código fuente. Ahora repasaremos cuáles son las herramientas que necesitaremos para desarrollar y ejecutar el juego, junto a las características de Java como lenguaje de programación. Herramientas de compilación y ejecución en Java Para trabajar en Java es necesario el entorno que permite desarrollar, realizar el precompilado y ejecutar el programa. Adicionalmente, si solo se ejecutará un programa se requiere el entorno de ejecución de Java, que incluye máquina virtual Java (JVM). ​Figura 2: Entorno de ejecución de Java Fuente: elaboración propia. Java Development kit (JDK): “es un kit que proporciona el entorno para desarrollar y ejecutar el programa Java” (Walton, s.f., https://javadesdecero.es/fundamentos/diferencias-jdk-jre-jvm/). Actualmente se encuentra en el paquete Java SE. Contempla herramientas de desarrollo y el JRE para ejecutar su programa java. Entre las herramientas tenemos, a saber (ver tabla 1). Tabla 1: Herramientas JDK Fuente: adaptado de Tumax, s.f. Java Runtime Environment (JRE): es el entorno de ejecución de Java y está conformado principalmente por la Java virtual machine (JVM) y las bibliotecas principales de java: bibliotecas de integración y bibliotecas de lenguajes. Java virtual machine (JVM): es un programa que simula los componentes primarios de una computadora. Los compiladores ‘just in time’ (JIT) dentro de la JVM permiten optimizar el uso. Las principales funcionalidades implementadas por la JVM son, a saber. Motor de ejecución: ejecuta el código bytecode generado por algún compilador de Java. Interfaz multihilos: proporciona el soporte para los multihilos que permiten ejecutar tareas en paralelo. Manejador de excepciones: agiliza las excepciones cuando se produce un error. Cargador de clases: su función es cargar dinámicamente las clases Java a partir de los archivos de clase (.class). Administrador de seguridad: se encarga de asegurar que las clases cargadas sean seguras, así como controlar el acceso a los recursos del sistema. Manejador de memoria: cuando el programa en Java crea una instancia de objeto, la JVM asigna automáticamente un espacio de memoria para ese objeto. El recolector de basura Java se ejecuta en background y recupera la memoria cuando ya no es necesaria para el objeto. Este proceso se denomina memoria implícita, porque no requiere una acción específica del programador. El recolector de basura es una de las funciones esenciales en Java. 4. Entornos de desarrollo El código en Java se puede escribir en cualquier editor de texto, y para compilar el código en bytecodes se puede utilizar el JDK. Un entorno de desarrollo integrado (IDE) proporciona servicios integrales que simplifican el desarrollo y depuración de un programa. Evita tener que realizar configuraciones manuales por separado, ya que dispone de herramientas tales como un editor, compilador, depurador, autocompletado de código, reconocimiento de sintaxis y soporta la instalación de plug-in o integraciones con sistemas de versionado. Algunas de las IDE más populares son: NetBeans, Eclipse, IntelliJ Idea, BlueJ y Xcode. En nuestro caso, los ejemplos los colocaremos con NetBeans, pero se puede utilizar cualquier IDE según las preferencias de cada uno. Características de Java Orientado a objetos. La programación orientada a objetos permite que el código sea reutilizable, organizado y fácil de mantener. Utiliza unidades lógicas llamadas objetos y sus interacciones para diseñar aplicaciones y programas. Los objetos se crean a partir de una plantilla que se denomina clase mediante un proceso de instanciación. Tienen atributos, que son los datos que le da la clase y métodos que son las funcionalidades que le da la clase. Flexibilidad. La flexibilidad que otorga la reutilización de código permite a los programadores disminuir los tiempos de desarrollo. La biblioteca de clases de Java contiene una colección de clases o grupo de clases llamadas paquetes. Cuando se define una clase dentro de un paquete, se evitan colisiones debido a que se accede con el nombre de ambos. Tanto las clases como los paquetes se pueden importar directamente desde un programa y esto disminuye los tiempos de desarrollo y la posibilidad de introducir errores. Multiplataforma. Los programas desarrollados en Java funcionan en cualquier entorno de hardware y sistema operativo, dado que no es el sistema quien los ejecuta, sino que lo interpreta la máquina virtual (Java virtual machine o JVM). Al ser un lenguaje interpretado puede percibirse, en algunos programas, como más lento, pero evita tener que compilarlos para cada sistema y es suficiente con que este tenga la máquina virtual. Código abierto. Java es un lenguaje de código abierto, ya que están disponibles prácticamente todas las librerías para que el programador pueda analizar el código fuente o ampliar su funcionalidad. Inclusive esto puede beneficiar a otros al resto de la comunidad. Simple, comparado con otros lenguajes. Java es parecido a C + + en cuanto a su estructura y sintaxis, pero sin incluir las características que eran más confusas. Por ejemplo, no es necesario liberar memoria, ya que un reciclador se encarga de ello, elimina los punteros (variable que almacena la posición de memoria), etcétera. Multihilo. Una ventaja principal del multihilo es que permite escribir programas muy eficientes, porque le permite utilizar el tiempo de inactividad que está presente en la mayoría de los programas. Java logra llevar a cabo varias tareas simultáneamente dentro del mismo programa, lo que permite mejorar el rendimiento y la velocidad de ejecución. La multitarea basada en hilos genera la necesidad de un tipo especial de característica llamada sincronización, que permite coordinar la ejecución de hilos de determinadas maneras bien definidas. Actividades de repaso 1) La máquina virtual de Java (JVM) se encarga de virtualizar el código fuente para que el programa corra en distintas plataformas. Verdadero Falso Justificación 2) El algoritmo implementado en Java (archivo.java) se pre compila al generar un código conocido como __________ (archivo.class). bytecode intérprete JVM Justificación Referencias Celaya, A. E. (2020). El éxito del Minecraft, cuando el éxito depende de uno. Recuperado de https://www.laprensa.com.ar/491992-El-exito-del-Minecraft-cuando-el-exito-depende-de- uno.note.aspx. Grupo C IS513 (s.f.). Línea de tiempo de los lenguajes de programación. Recuperado de https://acortar.link/DcuYv3. Instituto mexicano de contabilidad, administración y finanzas (2016). La esencia de la programación. Recuperado de https://imecaf.com/blog/2016/05/24/la-esencia-de-la- programacion/. Tumax, E. (s.f.). Fundamentos del lenguaje de programación. Introducción a Java. Recuperado de https://slidetodoc.com/fundamentos-del-lenguaje-de-programacin-introduccin-a-java/. Walton, A. (s.f.). Diferencias entre JDK, JRE y JVM. Recuperado de https://javadesdecero.es/fundamentos/diferencias-jdk-jre-jvm/. Sintaxis y tipos de datos en Java Introducción En esta lectura nos introduciremos en la sintaxis del lenguaje java, que se compone de la estructura y las reglas que deben seguirse al escribir el código fuente. Además, vamos a reconocer cuáles son los elementos que debemos tener en cuenta como mejores prácticas, evitar errores y facilitar el reuso de código: identificadores, separadores, palabras reservadas y paquetes. Finalmente, con el conocimiento de los tipos de datos primitivos y por referencia, lograremos una correcta utilización de variables y constantes. 1. Caso. El matemático que inventó hace más de 150 años la forma en que hoy busca Google Cada vez que haces una simple búsqueda en Google, o en cualquier otro buscador informático, entre los mecanismos de programación que hacen posible que encuentres lo que buscas hay unos principios de lógica que fueron concebidos hace más de 150 años. Fue el matemático inglés George Boole quien inventó un sistema de álgebra que es clave para la programación de hoy en día. ​ Se puede decir que los ladrillos con los que se construye la programación, que son los comandos o instrucciones que se le da a un sistema informático, están todos basados en la lógica de Boole. Durante los últimos 17 años de su vida George Boole estableció el concepto de lógica algebraica en matemáticas y simplificó el mundo en enunciados básicos que tenían por respuesta Sí o No, utilizando para ello aritmética binaria. Este concepto, que introdujo en 1847 y expandió siete años más tarde, es lo que está presente en los programas informáticos actuales. Boole utilizó el concepto de puertas lógicas, o preguntas, que exploran un enunciado. Las puertas lógicas más básicas son, en el lenguaje original de Boole, AND, OR o NOT. Es decir, Y, O o No en español. Después, estas tres puertas se pueden combinar para crear enunciados más complejos. Así que cuando buscas en internet "Miley Cyrus" hay un uso implícito de la lógica booleana del comando AND para combinar las dos palabras, "Miley" y "Cyrus". Mucho antes de Google, durante los primeros años en que se hacían búsquedas, era frecuente usar los comandos AND, OR y NOT para filtrar los resultados. Hoy, los avances en la tecnología de búsquedas hacen que muchas se puedan realizar utilizando un lenguaje más natural. Aun así, Google todavía permite a los usuarios escribir OR o incluir el símbolo de sustracción - para afinar los resultados. (BBC Mundo, 2015, https://www.bbc.com/mundo/noticias/2015/01/150106_tecnologia_algebra_matematicas_b oole_busquedas_google_programacion_ig) Presentación del caso Oracle está evaluando el relanzamiento de su buscador HotJava y, para ello, quieren comenzar a trabajar en un desarrollo que permita realizar la búsqueda mediante filtros básicos utilizando los comandos AND, OR y NOT. Sabemos que la mayoría de los usuarios actualmente está acostumbrado a utilizar un lenguaje más natural, pero esto sería un MVP (mínimo producto viable) que luego va a escalar. Para esto te piden que inicialmente realices un resumen de las consideraciones con respecto a sintaxis, estructura y elementos que se deben tener en cuenta para poder comenzar a escribir el código. Para poder llevar adelante esta tarea, comenzaremos por revisar juntos algunos conceptos importantes. 2. Sintaxis de Java Como ya vimos, un programa representa una secuencia ordenada de instrucciones entregadas a la computadora y siguiendo una lógica predeterminada por uno o más algoritmos. Es necesario un lenguaje de programación donde se escribe el código con las instrucciones que dan origen a la generación del programa “La sintaxis de un lenguaje de programación se define como el conjunto de reglas que deben seguirse al escribir el código fuente de los programas” (Nakayama C. y Solano Gálavez, s.f., p. 1) para su comprensión y ejecución. Java utiliza el estándar de lenguaje Unicode, que permite incluir caracteres específicos de la mayoría de los alfabetos. Particularmente, los primeros 128 caracteres son los correspondientes al conjunto ASCII (American Standard Code for Information Interchange), evitando así problemas de compatibilidad. Algunos caracteres comunes se codifican como bytes individuales y otros requieren dos o más bytes (por ejemplo, los ideogramas chinos, japoneses, coreanos). Unicode cuenta con vocales acentuadas, consonantes propias de los idiomas como por ejemplo la ñ. Estructura general de un programa Java La clase es la unidad fundamental de programación en Java. “Un programa en Java está formado por un conjunto de clases” (Entreunosyceros, s.f., https://entreunosyceros.net/introduccion- java/amp/), a partir de las cuales se crean los objetos que se relacionan entre ellos para lograr un objetivo o resolver un problema. La clase se considera como la plantilla a partir de la cual se crean los objetos. Los objetos tienen atributos que son los datos que le da la clase y métodos que son las funcionalidades o acciones que le da la clase. A nivel estructura, un programa en Java se compone necesariamente de una clase principal main () y puede contener declaraciones de clases, declaraciones de importación de paquetes, métodos definidos por el usuario dentro de las clases, comentarios del programa. Declaración import: los paquetes permiten agrupar clases comunes y se encuentran en un directorio que tiene el mismo nombre. Una clase puede definirse dentro de un paquete y puede usar clases definidas en ese o en otros paquetes. “Para incorporar y utilizar las clases de un paquete en un programa se utiliza la declaración import” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 48). El paquete puede estar definido en las API de Java o puede ser uno creador por el programador y la sintaxis es: import nombrePaquete.nombreClase; Comentarios: es información complementaria utilizada para incluir datos que son ignorados por el compilador. Su uso es opcional, pero recomendable como parte de las buenas prácticas para facilitar la lectura del programa. Otra buena práctica de programación es comentar su programa en la parte superior de cada archivo fuente; la información que puede incluir es: nombre de archivo, nombre del programador, descripción breve, fecha en que se creó la versión e información de la revisión. En Java los comentarios de un programa se pueden introducir de dos formas: Con los caracteres para insertar más de una línea. Con la secuencia de dos barras (//) para insertar una línea. (Joyanes Aguilar y Zahonero Martínez, 2011, p. 53) “Método main (): cada programa Java tiene un método main (), que es el estándar utilizado por la JVM para iniciar la ejecución de cualquier programa Java. Dicho método se conoce como punto de entrada de la aplicación Java” (García Flores, 2020, https://la- respuesta.com/contribuyendo/como-hacer-un-metodo-main-en-java/) y su estructura es: public static void main(String [] ar) { sentencias } “Un programa puede tener solo un método main ()” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 50) y debe estar declarado como public static void. “Las sentencias situadas en el interior del cuerpo de main (), u otro método deben terminar con punto y coma” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 50). Declaración de clases: además del método main () el programa puede tener otras clases. Se declaran con la palabra reservada class, seguida por el nombre debe ser un indicador válido y, por convención, comenzar con una letra mayúscula. El archivo donde se guarde el programa debe tener el mismo nombre que la clase principal, es decir, la que contiene main () y su extensión es.java Métodos definidos por el usuario: los programas en Java se realizan a partir de una o más clases compuestas por variables y métodos. A su vez, los métodos contienen una o más sentencias, creadas para realizar una tarea. Es posible crear n métodos en una clase Java. Todos los métodos tienen el tipo de dato que devuelven (void es que no devuelve valor), nombre y una lista de atributos o argumentos. Aunque se puede asignar cualquier nombre se debe intentar que sean representativos de su propósito. “Los métodos en Java se especifican en la clase a la que pertenecen; su definición es la estructura del mismo” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 51). tipo_retorno nombreMetodo (argumentos) { sentencias cuerpo del método return } ​ Elementos de un programa en Java “Todo programa en Java consta de un archivo donde se encuentran las clases y métodos que escribe el programador” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 54) y eventualmente otros archivos donde se encuentran los paquetes con las otras clases utilizadas. “El compilador traduce cada archivo con su programa, además incorpora las clases solicitadas al programa y analiza” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 54) los elementos léxicos del programa, que son los siguientes: Identificadores: “el programador tiene libertad para elegir el nombre de las variables, clases, métodos y otros elementos del programa” (Martínez Ladrón de Guevara, s.f., p. 14). “El identificador es una secuencia de caracteres, letras, dígitos, subrayados (_) y el símbolo $.” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 54). Puede ser de cualquier longitud, el primer carácter no puede ser un número. Los identificadores no pueden contener caracteres especiales que son: + - * / = % & # ! ? ^ “ ‘ ~ \ | ()[]{}:;., Como recomendación, para la escritura en Java se recomienda utilizar CamelCase, ya que hace más legible el código. Por ejemplo, si necesitamos escribir la variable tamaño_letra se recomienda utilizar tamañoLetra. Los nombres de las clases, los métodos y las variables son todos identificadores. Por convención, usan la nomenclatura lomo de CamelCase, [ya que las mayúsculas sobresalen de forma similar a la joroba de un camello]. Además, por convención los nombres de las clases comienzan con una letra mayúscula, los nombres de los métodos y variables comienzan con una letra en minúscula. (Deitel y Deitel, 2016, p. 72) Java es sensible a las mayúsculas, por lo que los identificadores letra y LETRA son distintos; se recomienda usar siempre el mismo estilo con las siguientes buenas prácticas: “Identificadores de variables en minúsculas. Constantes en mayúsculas. Métodos en minúsculas. Clases con el primer carácter en mayúsculas” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 55). Separadores: Este grupo de caracteres se utiliza de diferentes formas: para indicar que un identificador es un método o una matriz, para especificar una determinada operación aritmética, lógica o de relación, etc. Son los siguientes: !%$&*()-+={}~^|[]\;´_?,./" Los separadores son espacios en blanco, tabulaciones, retornos de carro y avances de línea. Palabras reservadas: son palabras asociadas con algún significado especial en Java y “no se puede utilizar como nombre de identificador, clase, objeto o método” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 55). Se escriben siempre con minúsculas. Tabla 1: Palabras reservadas Java Palabras reservadas Java Abstract continue for new switch Assert default goto package synchronized Boolean do if private this Break double implements protected throw Byte else import public throws Case enum instanceof return transient Catch extends int short try Char final interface static void Class finally long strictfp volatile Const float native super while Fuente: elaboración propia. Un literal es un valor constante representado por una secuencia de caracteres. Vamos a analizar en detalle sus características más adelante, pero por el momento es importante identificar que en Java son literales reservados: true, false y null. De la tabla anterior, en Java no se utilizan los comandos const y goto. Sin embargo, se mantuvieron como palabras reservadas para generar los mensajes de error. Paquetes: son una agrupación de clases que tienen funcionalidades y elementos comunes. Ayudan a organizar las clases en una estructura de carpetas con el mismo nombre del paquete, lo que facilita su ubicación y reutilización. “Todas las clases en Java pertenecen a algún paquete, [solo que] cuando no se declara un paquete especifico se usa el predeterminado (global)” (Walton, s.f., https://javadesdecero.es/intermedio/paquetes-en-java/) que no tiene nombre y por lo tanto es transparente. Aunque el paquete predeterminado va a funcionar, en aplicaciones reales siempre es recomendable definir paquetes. Una clase se declara perteneciente a un paquete con la sentencia package incluida como una línea de código: package nombrePackage; ​ “El nombre de una clase debe ser único dentro del paquete donde se define” (López Goytia y Gutiérrez González, 2014, p. 5), pueden coexistir si están en dos paquetes distintos. Existen “una serie de paquetes predefinidos” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 56), por ejemplo: java.lang (clases nucleares), java.io (clases de entrada y salida), java.util (clases de utilidad). Además, el programador puede crear paquetes propios para almacenar clases creadas y después utilizarlas cuando sea necesario. “Primero resuelve el problema. Entonces, escribe el código" (John Johnson en Superprof, 2021, https://www.superprof.es/blog/como-aprender-a-programar- desde-cero/). Si retomamos el caso y reconocemos la sintaxis, estructura y elementos de un programa Java vamos a validar cómo se debe trabajar con los tipos de datos para asegurar que las operaciones lógicas se puedan realizar de manera adecuada. 3. Tipos de datos en Java Las variables representan un espacio de memoria a la que asignamos un contenido. “Las variables del tipo primitivas nos permiten almacenar valores [elementales] como números, caracteres, [valores verdadero o falso]. Las variables de tipo referencia no almacenan valores, sino que nos permiten acceder a los atributos y métodos de los objetos” (Belmonte Fernández, Granell Canut y Erdozain Navarro, 2012, p. 33). Para englobar ambos conceptos se utiliza el término tipo de datos. Entonces, “un tipo de datos es el conjunto de valores” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 57) primitivos (elementales) o una referencia a métodos y atributos de un objeto. En Java son muy importantes debido a que el compilador comprueba la compatibilidad de los tipos de datos en todas las operaciones. Esto ayuda a la confiabilidad del código debido a que los tipos incompatibles no se compilan y para poder hacerlo se requiere que todas las variables, expresiones y valores tienen un tipo de dato. Resumiendo, en Java toda la información que maneja un programa está representada por dos tipos principales de datos: primitivos y por referencia. Tipo de datos primitivos: en Java casi todo es un objeto, una excepción es este tipo de datos primitivos y pueden utilizarse directamente en un programa sin necesidad de crear objetos. Los demás tipos de datos se crean a partir de los primitivos. “Como mencionamos anteriormente, un tipo de dato determina los valores que pueden asignarse, el formato de representación correspondiente y las operaciones que pueden realizarse” (García- Beltrán, Arranz, 2007, p. 32). Los tipos de datos primitivos en Java son: ⮚ Enteros, números completos y sus negativos, de tipo int. ⮚ Variantes de enteros, tipos byte, short y long. ⮚ Reales, números decimales: tipos float, double. ⮚ Caracteres, letras, dígitos, símbolos y signos de puntuación. ⮚ Boolean, true o false. (Joyanes Aguilar y Zahonero Martínez, 2011, p. 57) En la siguiente tabla, se muestran los tipos de datos primitivo: Tabla 2: Tipos de datos primitivo Valor Forma de Tipo Tamaño Valor mínimo Valor máximo defecto inicializar boolean 1 bit FALSO boolean a = true True-False char 16 bits Null char a = ‘B’ Unicode byte 8 bits 0 byte a = 0 -128 127 short 16 bits 0 short a = 10 -32768 32767 int 32 bits 0 int a = 1435 -2147483648 2147483647 long 64 bits 0 long a = 140000 -9.22337E+18 9.22337E+18 float 32 bits 0 float a = 4.2 -3.40E+44 3.40E+44 double 64 bits 0 double a = -1.7976E+308 1.7976E+308 135.3234 Fuente: elaboración propia. La declaración de una variable consiste en definir el tipo de dato y el identificador. La asignación permite fijar valores a las variables, teniendo en cuenta el tipo de datos utilizado en la declaración. “Si una variable tiene un valor previo y se realiza una asignación, el valor que tenía se reemplazará con el nuevo” (techkrowd, s.f., https://techkrowd.com/2018/05/17/java-operador- de-asignacion/). La declaración y asignación de variables se realiza de la siguiente forma: Declaración = Declaración y asignación ,... Declaración múltiple int entero; // definición de entero entero = 5; // asignación de entero float flotante = 3.52 // definición y asignación de flotante Tipos de datos por referencias: “indican que vamos a trabajar con instancias de clases [(objetos), no con datos primitivos. Es decir que,] una variable de tipo referencia establece una conexión hacia un objeto, y a través de esta conexión podemos acceder a sus atributos y métodos” (Belmonte Fernández, Granell Canut y Erdozain Navarro, 2012, p. 33). ⮚ Librerías estándar: son las clases y métodos ya disponibles en las API de Java. ⮚Tipos definidos por el programador: son las clases y métodos definidos por el programador. ⮚ Tipo envoltorio: en ocasiones es útil tratar los tipos de datos primitivos como objetos y para ello “el API de Java incorpora las clases envoltorios (wrapper class)” (Abelp, s.f., http://www.abelp.net/apuntesjava/17wrapper.html) por cada dato primitivo: Tabla 3: Tipo primitivo y tipo envoltorio Tipo primitivo Tipo envoltorio boolean Boolean char Character byte Byte short Short int Integer long Long float Float double Double Fuente: elaboración propia. Aunque eventualmente podrían contener misma información, la selección de un tipo de dato primitivo o envoltorio se debe realizar dependiendo de las necesidades del programa. Por ejemplo, un envoltorio es de utilidad cuando se necesita hacer uso de los métodos existentes para estas clases y convertir cadena de caracteres en números; su nombre se escribe con la primera letra en mayúscula. Hay que tener en cuenta que las operaciones aritméticas habituales están definidas solo para los datos primitivos por lo que las clases envoltorio (wrapper) no pueden utilizarse en estos casos. Además, las variables primitivas tienen mecanismos de reserva y liberación de memoria más eficaces y rápidos que los objetos por lo que deben usarse datos primitivos en lugar de sus correspondientes envolturas siempre que se pueda. (Abelp, s.f., http://www.abelp.net/apuntesjava/17wrapper.html) A continuación, una breve descripción con las principales características de los tipos de datos en Java. Enteros: byte, short, int y long Es uno de los tipos de datos más conocidos, ya que permiten trabajar con números. “En Java hay cuatro tipos de datos enteros: byte, short, int y long enumerados de menor a mayor rango” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 58). Las operaciones aritméticas de enteros de tipo int se realizan en 32 bits, por lo que la definición de las variables debe ser consistente al tipo de datos utilizado. El siguiente ejemplo entregaría un warning: short x; int a = 18, b = 3; x = a+b; “Al devolver a+b, no se puede asignar a x un valor de tipo int porque es de tipo short” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 59). Con una conversión de datos también se podría resolver en este caso: x = (short) (a+b) “Las constantes enteras siempre se consideran de tipo int por defecto, para que el compilador las considere como long se le agrega el sufijo l o L” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 59); por ejemplo: long a = -1234567L Coma flotante: float, double Los tipos de datos de coma flotante representan números que contienen una coma (float), tales como 3.123 o números grandes (double) como 1.55*10^15. La declaración de las variables es igual de coma flotante es igual que la de variables enteras: float valor; float valor1, valor2; float valor = 11.99f; double producto; Las constantes con coma flotante se consideran del tipo double por defecto, para que el compilador las considere como float se le agrega el sufijo l o L; por ejemplo: float a=1.2345F. Caracteres: char Un carácter es cualquier elemento de un conjunto símbolos disponibles en el estándar Unicode. Por este motivo, los elementos char utilizan 16 bits, el doble que varios de los lenguajes de programación. La declaración de las variables char se realiza de la siguiente manera: char datoCaracter; char letra = 'A'; “De manera interna, los caracteres se almacenan como números [según la codificación correspondiente]. Por ejemplo, la letra A se almacena como 65, la B como 66, (…) etc. (…)De esta manera, se pueden realizar operaciones aritméticas” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 61): Por ejemplo, la letra A es el código 65 y la letra a es el código 97, entonces: char letra = ‘a’; letra = (char) (letra – 32); // se carga entonces en la variable la letra A Tipo de dato void “Los métodos devuelven un valor de un tipo de datos determinado: int, double, etc.” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 62) “La palabra reservada void se utiliza para indicar que el método no devuelve un valor” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 51): void mostrar (); Sin embargo, en Java no pueden definirse variables del tipo void, ya que el compilador lo va a detectar como error. Solo se puede utilizar como tipo de retorno de un método que no devuelve valor. Boolean “Java incorpora el tipo de dato boolean cuyos valores son verdadero (true) y falso (false). Las expresiones lógicas devuelven valores de este tipo” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 61). La declaración de las variables boolean se realiza de la siguiente manera: “boolean caracteristica; característica = true; boolean referencia, bandera” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 61); El uso de operadores lógicos y relacionales permite evaluar expresiones y devolver true o false. resultado = (x>3) && (x " + n2); else System.out.println(n1 + " < " + n2); System.out.println("\nResultado con operador condicional ?: "); float mayor = n1 > n2 ? n1 : n2; System.out.println("El número mayor es: " + mayor); } } Operador instanceof “Con frecuencia se necesita conocer la clase de la que un objeto es instancia” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 215). En Java existe el operador binario instanceof que permite evaluar si el objeto es de una determinada clase. Tiene dos operandos, el primero es el objeto y el otro es la clase. Evalúa la expresión como verdadera si el objeto es una instancia de la clase. String s = new String("Objeto s de la clase String"); if (s instanceof String) System.out.prinln("El objeto s pertenece a la clase String"); Puede ocurrir que el objeto que estamos evaluando con instanceof, no sea una instancia directa de la clase que aparece a la derecha del operador. Sin embargo, instanceof devolverá verdadero si el objeto es de un tipo compatible con el objeto que aparece a su derecha. Por ejemplo, una instancia de una subclase. class Animal {} class Perro extends Animal { public static void main (String[] args){ Perro pluto = new Perro(); if (pluto instanceof Animal) System.out.println("pluto es un perro y también un animal"); } } 3. Conversiones de tipos (Casting) En algunas ocasiones es necesario convertir un tipo de datos sin cambiar el valor que representa. Las conversiones explícitas son especificadas por el programador y las implícitas las realiza Java automáticamente cuando: Se asigna un valor de un tipo numérico a una variable de otro tipo numérico, siempre que no haya pérdida de información. Se combinan tipos mixtos en expresiones. Se pasan argumentos a métodos siempre que no suponga una pérdida de precisión. (Joyanes Aguilar y Zahonero Martínez, 2011, p. 100) Conversión implícita: los tipos fundamentales pueden mezclarse y las conversiones se realizan automáticamente al tipo compatible más alto (Joyanes Aguilar y Zahonero Martínez, 2011, p. 101). En la siguiente tabla se presentan las posibles conversiones implícitas. Tabla 10: Posibles conversiones implícitas Nuevo tipo Tipos origen short byte int byte, short, char long byte, short, char, int float byte, short, char, int, long double byte, short, char, int, long, float Fuente: techkrowd, s.f., recuperado de https://techkrowd.com/2017/12/31/java-conversion-de-tipos-casting/. int i = 92; double x = 54,5; x = x + i; //valor de i se convierte en double antes de sumar “Conversión explícita: en el casting explícito es tarea del programador especificar el nuevo tipo al que se va a transformar el dato. Se escribe de forma explícita entre paréntesis delante del dato” (Techkrowd, s.f., https://techkrowd.com/2017/12/31/java-conversion-de-tipos-casting/): (tiponombre) valor // convierte valor a tiponombre (float) i; // convierte i a float “El operador molde (tipo) tiene la misma prioridad que los unarios +, - y !” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 101) “Hay que tener cuidado al realizar esta conversión, ya que se puede aplicar a tipos no compatibles, lo que puede derivar en pérdidas de información e incluso errores en ejecución” (techkrowd, s.f., https://techkrowd.com/2017/12/31/java-conversion-de-tipos-casting/). float x = 5.7F; int y = (int) x; “En este caso se realizará la conversión, pero se perderá la parte decimal del número con punto flotante al guardarlo en y” (techkrowd, s.f., https://techkrowd.com/2017/12/31/java-conversion-de- tipos-casting/). Concatenación de cadena de caracteres Para hacer más simple el manejo de las cadenas, Java tiene un operador suma que permite concatenar dichas cadenas. Ejemplo: "Universidad" + " Siglo " + 21 Permite obtener a la salida la cadena “Universidad Siglo 21” Esta redefinición se conoce como sobrecarga del operador + y realiza conversiones automáticamente “sin necesidad de emplear métodos de conversión” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 102). Esta propiedad es muy útil en salidas con el método print (). Sin embargo, se debe tener cuidado de no utilizar dos operandos numéricos, ya que la salida sería la suma y no la concatenación. int x = 9; int y =10; System.out.println(x + y); La salida en este caso será el número 19, ya que es el valor de x+y transmitido a println (). Retomando el caso, vemos que es muy importante conocer los tipos de operadores con los que se va a trabajar para poder realizar correctamente asignaciones, realizar operaciones aritméticas, trabajar con operadores relacionales principalmente por la utilidad que esto luego va a tener en las estructuras condicionales e iterativas y cómo convertir tipos de datos o concatenar cadenas. Ahora vas a proponer un pequeño código para Engeman® que permita Monitorear servicios en ejecución. Los datos de entrada van a ser: Días desde el último mantenimiento Cantidad de piezas fabricadas Si los días desde el último mantenimiento >180 o la cantidad de piezas fabricadas es >50.000 debe presentarse en pantalla un mensaje de que la máquina necesita un mantenimiento. Diseña el algoritmo e implementa en NetBeans, compara luego con la solución propuesta. package engemanmantenimiento; import java.util.Scanner; public class EngemanMantenimiento { public static void main(String[] args) { int diasMant, cantidadPiezas; Scanner entrada = new Scanner(System.in); System.out.print("Introduzca la cantidad de días desde el último mantenimiento: "); diasMant = entrada.nextInt(); System.out.print("Introduzca la cantidad de piezas fabricadas: "); cantidadPiezas = entrada.nextInt(); System.out.println("Resultado con if...else"); if (diasMant > 180 || cantidadPiezas > 50000) System.out.println("SE REQUIERE MANTENIMIENTO PREVENTIVO"); else System.out.println("SE PUEDE CONTINUAR CON LA PRODUCCIÓN"); } } 4. Sentencias de control de flujo Las sentencias de control de flujo determinan el comportamiento del programa, ya que permiten combinar instrucciones de acuerdo con una condición lógica que tiene un punto de entrada y otro de salida. Podemos encontrar dos tipos: condicionales e iterativas. Se suelen denominar también las estructuras de control, ya que permiten modificar el flujo de ejecución de las instrucciones de un programa. Sentencias condicionales Sentencia if: La estructura condicional o de control de selección principal es una sentencia if, que tiene dos alternativas posibles. if (expresión) Acción Se evalúa la expresión y si es verdadera ejecuta la Acción, en caso contrario sigue con la siguiente línea de sentencia. Acción puede ser una sentencia simple o compuesta. package positivo; import java.util.Scanner; public class Positivo { public static void main(String[] args) { float numero; Scanner entrada = new Scanner(System.in); System.out.println("Introduzca un número real"); numero = entrada.nextFloat(); if (numero > 0) System.out.println(numero + " es mayor que cero"); } } Sentencia if-else: en este caso la estructura condicional tiene dos acciones posibles, si la expresión es verdadera se ejecuta la primera de lo contrario la segunda. if (expresión) acción 1 else acción 2 Si las acciones son una única línea se finaliza con un “;” de lo contrario se incluyen llaves. package positivo; import java.util.Scanner; public class Positivo { public static void main(String[] args) { float numero; Scanner entrada = new Scanner(System.in); System.out.println("Introduzca un número real"); numero = entrada.nextFloat(); if (numero > 0) System.out.println(numero + " es mayor que cero"); else System.out.println(numero + " es menor que cero"); } } Sentencia switch case: esta estructura se utiliza para seleccionar una de múltiples opciones de acuerdo con la expresión de control definida. switch (expresionControl) { case etiqueta1 : sentencias1 ; break; case etiqueta2 : sentencias2 ; break; case etiquetan : sentenciasn ; break; default: sentenciasd ; // opcional } La expresión Control se evalúa y compara con cada una de las etiquetas de case. Debe ser de tipos de datos primitivos byte, short, char e int; a partir de JDK7 funciona con tipos enumerados (enum en java), la clase String y las clases envoltorio (wrapper). No puede ser una condición. Cada etiqueta es un valor único y debe ser diferente de los otros. Si el valor de la expresión Control es igual a una de las etiquetas case, entonces se ejecutan las sentencias correspondientes a este caso. Comienza por la primera etiqueta hasta el final del switch o hasta encontrar un break. “El tipo de cada etiqueta debe ser el mismo que la expresión” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 118) Control. Si el valor no está en el listado de etiquetas se puede ejecutar una sentencia default, que igualmente es opcional, de lo contrario no se ejecuta ninguna sentencia. Break altera el flujo de esta selección múltiple, ya que si se encuentra se finaliza el bucle. package sentenciacase; import java.util.Scanner; public class SentenciaCase { public static void main(String[] args) { Scanner entrada = new Scanner(System.in); int tipo_educacion; System.out.println("Introduzca tipo de educacion. 1-Primaria. 2-Secundaria. 3-Terciaria: "); tipo_educacion = entrada.nextInt(); switch(tipo_educacion) { case 1 -> System.out.println("Educación primaria"); case 2 -> System.out.println("Educación secundaria"); case 3 -> System.out.println("Educación terciaria"); default -> System.out.println("Educación no soportada"); } } } Sentencias iterativas “Las estructuras iterativas se denominan comúnmente bucles y cada repetición de sentencias de un bucle se llama iteración” (Departamento de Lenguajes y Sistemas Informáticos, s.f., p.2). Son fundamentales para poder repetir ciertas sentencias de código, tanto “un número determinado de veces como mientras se cumpla una condición” (EducandoconTIC, s.f., https://educandocontic.com/estructuras-repetitivas/). Java soporta tres estructuras de este tipo: los bucles while, for y do-while. Bucle while: tiene una condición dada por una expresión lógica que controla la secuencia de repetición. Se evalúa antes de ejecutar el bucle “La ejecución de la sentencia o sentencias se repite mientras la condición del bucle permanece verdadera y termina cuando se hace falsa” (Joyanes Aguilar, Castillo Sanz, Sánchez García, Zahonero Martínez, 2005, p. 71). Como condición se evalúa antes de ejecutar el bucle, para ejecutarse debe ser al menos una vez verdadera. while (condicion_bucle) { sentencia-1; sentencia-2; sentencia-n; } Se evalúa la condición bucle, si es verdadera se ejectuan las sentencias del bucle y vuelve a evaluar la condicion_bucle. Si es falsa transfiere el control a la sentencia posterior al bucle while. package buclewhile; public class BucleWhile { public static void main(String[] args) { int contador = 0; while(contador < 10) { contador ++; System.out.println("contador: " + contador); } System.out.println("Finalizado, número de veces: " + contador); } } Bucle for: es una estructura de control cíclica que permite ejecutar una o varias sentencias de manera iterativa, teniendo el control del número de repeticiones. Este bucle “es la mejor forma de programar la ejecución de un bloque de sentencias un número fijo de veces” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 143) for (Inicializacion; CondicionIteracion; Incremento) La Inicialización define cuál es el valor inicial de la variable, la CondicionIteracion es una “expresión lógica que itera las sentencias mientras la expresión sea verdadera” (Joyanes Aguilar y Zahonero Martínez, 2011, p. 143), y el Incremento es en cuanto se incrementa o decrementa la variable de control de bucle. package buclefor; public class BucleFor { public static void main(String[] args) { int maximo = 20; int suma = 0; for (int n = 1; n RETIROMIN){ this.saldoCuenta-=extraccion; } elseSystem.out.println("No se puede Retirar menos de: $ "+RETIROMIN); } @Override publicvoidinformacion() { System.out.println("Hola:" +this.nombreCliente); System.out.println("Su Saldo actual es:" + consultarSaldo()); } } Menu principal package fintech; import java.util.Scanner; public class MenuPrincipal { public static void main(String[] args) { Scanner sc = new Scanner(System.in); Transaccion t; intopcion; t = new Cuenta(1000,"Maria"); System.out.println("OPERACIONES BANCARIAS"); do{ System.out.println("1.-Transferencia"); System.out.println("2.-Retiro"); System.out.println("3.-Consulta"); System.out.println("Ingreseopcion:"); opcion = sc.nextInt(); switch (opcion){ case 1 -> { System.out.println("Ingresemontotransferencia:"); intmontoTran= sc.nextInt(); t.transferencia(montoTran); } case 2 -> { System.out.println("Ingresemontoretiro:"); intmontoRet= sc.nextInt(); t.retiro(montoRet); } case 3 -> { t.informacion(); } } System.out.println("1.Continua\n9.Salir"); opcion = sc.nextInt(); }while(opcion != 9); } } Actividad de repaso Una interfaz es similar a una clase convencional, ya que pueden tener atributos, métodos, constructores; pero la principal diferencia es que debe contar con al menos un método abstracto. Es verdadero, las interfaces deben contar con al menos un método abstracto. Es falso, las interfaces deben contar con todos sus métodos abstractos. Justificación Referencias Desarrolloweb.com. (s.f.). Métodos y atributos static en Programación Orientada a Objetos. Recuperado de https://desarrolloweb.com/articulos/metodos-atributos-static-poo.html Programando o intentándolo. (s.f.). This y super en Java. Recuperado de https://programandoointentandolo.com/2018/10/this-y-super-java.html. Walton, A. (s.f. a). Interfaces en Java con ejemplos. Recuperado de https://javadesdecero.es/intermedio/interfaces-ejemplos/ Walton, A. (s.f. b). Palabra Clave “static” en Java. Recuperado de https://javadesdecero.es/palabra- clave/static/#:~:text=Cuando%20un%20miembro%20se%20declara,miembro%20est%C3%A1ti co%20es%20main()%20 Walton, A. (s.f. c). Palabra Clave “super” en Java. Recuperado de https://javadesdecero.es/palabra-clave/super/ Revisión ☰ Inicio a la programación orientada a objetos En Java, podemos definir objetos que manipulan información para hacerlos interactuar con otros objetos y resolver un problema. La estructura de un objeto está determinada por la clase a la que pertenece, la cual define los atributos y métodos que soportan todas las instancias de su tipo. Un objeto representa una entidad real o abstracta que cumple una función definida en el dominio del problema que estamos representando con el programa. Los programas en Java se componen de muchos objetos que interactúan entre sí, y todos tienen tres características o propiedades: estado, comportamiento e identidad. Una clase es una plantilla a partir de la cual se crean objetos mediante un proceso que llamamos instanciación. Cuando se construye un objeto de una clase, se dice que se ha creado una instancia y todos los objetos que se derivan de la misma clase tienen características similares. “Las variables de instancia son no estáticas y se declaran en una clase fuera de cualquier método, constructor o bloque” (Walton, s.f. b, https://bit.ly/3tD1hC0). Las variables estáticas también se conocen como variables de clase. Se declaran de forma similar a las variables de instancia, utilizando la palabra reservada static. Una variable definida dentro de un bloque, método o constructor se llama variable local. ☰ Constructores y herencia en Java El constructor es un método especial que se define en una clase y es utilizado para la creación e inicialización de un objeto. No retorna ningún valor y su nombre es el mismo que la clase que lo implementa. Toda clase contiene siempre al menos un constructor definido implícita o explícitamente. La principal función del constructor es reservar memoria e inicializar las variables miembros de la clase. La herencia en Java es “… la capacidad de crear clases que adquieren de manera automática los atributos y métodos de otras clases que ya existen” (Walton, s.f. b, https://bit.ly/3umpVq0). “Si una clase deriva de otra, esta hereda sus atributos y métodos. Adicionalmente, puede añadir nuevos atributos y métodos, o redefinir los heredados” (Geek, 2019 b, https://bit.ly/3upzT9V). “La herencia permite a una clase incorporar otra clase en su declaración. Esto se hace mediante el uso de la palabra clave extends. Por lo tanto, la subclase se extiende a la superclase” (Walton, s.f. c, https://bit.ly/3umpVq0). “Java define una clase especial llamada java.lang.Object, que es una superclase implícita de todas las demás clases. Esto significa que una variable de referencia de tipo object puede referirse a un objeto de cualquier otra clase” (Walton, s.f. a, https://bit.ly/3qwVTys). En Java, cuando una subclase se extiende de una superclase, hereda sus métodos. “La redefinición de métodos es una característica que permite que una subclase proporcione una implementación específica de un método que ya está provisto por la superclase” (Walton, s.f. e, https://bit.ly/3uoGgui). ☰ Paquetes, modificadores y método main “Restringir el acceso a los miembros de una clase es una parte fundamental de la programación orientada a objetos” (Walton, s.f. d, https://bit.ly/3Nlyqdi). Si permitimos el acceso a datos privados solo a través de un conjunto de métodos bien definidos, podemos evitar que se asignen valores incorrectos, por ejemplo, mediante una verificación de rango. Un paquete es una forma utilizada por Java “… para agrupar de alguna manera lógica los componentes de nuestra aplicación que estén relacionados entre sí” (ProgramarYa, s.f., https://bit.ly/3D6hz9I) y mantener organizado nuestro proyecto. Los modificadores de acceso son palabras claves utilizadas para controlar el acceso a los datos que conforman un objeto o instancia. De esta manera, una clase y sus instancias que utilicen modificadores de acceso son objetos encapsulados. En Java, existen cuatro modificadores de acceso para los miembros de una clase: público, protegido, por defecto y privado. El encapsulamiento permite a los objetos elegir qué información es publicada y qué información es ocultada al resto de los objetos. Para hacerlo, los objetos presentan sus métodos como interfaces públicas y sus atributos como datos privados e inaccesibles para evitar su exposición. (TutoJava, s.f., https://bit.ly/3L314hA). “El método main es el punto de entrada de una aplicación Java y es el estándar utilizado por la JVM para iniciar la ejecución de cualquier programa”. (El método main en Java, s.f., https://bit.ly/3qu9UwU). ☰ Miembros estáticos, sobrecarga de métodos y abstracción Los elementos estáticos (o miembros de clase) son aquellos que pertenecen a la clase, en lugar de pertenecer a un objeto en particular. “Cuando un miembro se declara estático, se puede acceder antes de crear cualquier objeto de su clase y sin referencia a ningún objeto. Se declara con la palabra clave static” (Walton, s.f. f, https://bit.ly/3wzT0Rx). La sobrecarga de métodos es una estrategia de Java que permite que existan varios métodos en una clase con el mismo nombre, pero con diferentes tipos y/o número de parámetros. No es suficiente que dos métodos difieran solo en sus tipos de devolución, aunque pueden hacerlo. (Programación con Java, s.f., https://bit.ly/3Ldn61i). “Las palabras reservadas this y super de Java nos sirven para poder acceder a los atributos, métodos y constructores de la clase en la que se encuentran y de la clase padre, respectivamente” (Programando o intentándolo, s.f., https://bit.ly/36iRLLT). Java proporciona modificadores para implementar la abstracción en clases y métodos. Una clase abstracta proporciona una abstracción parcial en la que al menos uno de sus métodos no es abstracto. “La interfaz proporciona una abstracción completa, es decir que solo proporciona prototipos de métodos y no su implementación” (myservername, s.f., https://bit.ly/3tzR49r). Las clases abstractas tienen la característica de que no se pueden instanciar. “La estructura es prácticamente igual a una clase convencional, pero la principal diferencia es que debe contar con al menos un método abstracto” (García Pérez, s.f., https://bit.ly/3NkSwnY). “Las interfaces son clases completamente abstractas que contienen solo una colección de métodos abstractos y propiedades constantes” (Geek, 2019 a, https://bit.ly/36jHZZT). Polimorfismo y comparación de objetos Introducción En esta lectura, vamos a reforzar el concepto de polimorfismo, enfocando el análisis en casos de usos concretos que nos permitan validar su aplicación y, principalmente, trabajando en conjunto con la herencia. Cuando utilizamos variables con referencias polimórficas, solamente podemos invocar aquellos métodos definidos en la clase base a la que pertenece la variable de referencia. Vamos a utilizar el casting como alternativa para acceder a estos métodos de la subclase. Para poder comprender cómo podemos realizar una comparación de objetos, debemos tener en claro el manejo de la memoria. Trabajaremos nuevamente este concepto. Verificaremos la diferencia entre comparar variables de referencia y utilizar los métodos equals y hashcode. Adicionalmente, vamos a mostrar las ventajas de sobrescribir estos métodos para que se ajusten a nuestro modelo de negocios dado por el criterio de igualdad que nos interese definir. 1.Caso lectura 3: Utilización de polimorfismo y comparación de objetos para sistema de control de stock en supermercado Caso: sistema de control de stock en supermercado Recordemos que un supermercado está trabajando en la transformación digital de su casa matriz con el objetivo de generar valor en sus procesos internos y asegurar la disponibilidad de productos demandados por sus clientes. Uno de los procesos que necesita mejorar es el control de stock y te contrata para adecuar su sistema. En esta parte del caso, tu objetivo es identificar cómo aprovechar el polimorfismo y considerar una comparación específica solicitada como parte del MVP: si el producto de la lista es leche, se debe mostrar un cartel que pida validar la fecha de vencimiento. A continuación, trabajaremos con los conceptos que nos van a permitir fundamentar la decisión. Polimorfismo Es la capacidad que tienen los objetos de una clase de dar una respuesta distinta al mismo mensaje a partir de los parámetros utilizados durante su invocación. “Adquiere su máxima potencia cuando se utiliza en herencia, donde se establece una ligadura dinámica de los métodos” (Martín Velasco, 2019, https://bit.ly/3IRpMjw). Por esta razón, no es preciso decidir el tipo de objeto hasta el momento de la ejecución. Posibilita que un objeto de una clase se comporte igual que un objeto de cualquiera de sus subclases mientras se ejecuta el programa. Recordemos que la ligadura es la conexión que tiene lugar durante una llamada a un método: si se lleva a cabo durante el proceso de compilación, se llama ligadura estática; si ocurre en el proceso de ejecución, se llama ligadura dinámica. La ligadura dinámica hace posible que sea el tipo de objeto instanciado, obtenido mediante el constructor utilizado para crear el objeto, y no el tipo de la referencia, indicado en la declaración de la variable que apuntará al objeto, lo que determine qué versión del método va a ser invocada. El tipo de objeto al que apunta la variable de tipo referencia solo podrá ser conocido durante la ejecución del programa y por eso el polimorfismo necesita la ligadura dinámica. En Java el enlazado es dinámico en tiempo de ejecución, con la excepción de los métodos declarados static, final o sobrecarga, que se enlazan con ligadura estática. El polimorfismo en Java se logra mediante la sobreescritura de métodos y es considerado un polimorfismo en tiempo de ejecución porque el método que se ejecutará depende del objeto que es usado para invocarlo. A esta sobreescritura de métodos se la conoce también como redefinición de métodos (overriding), y es una característica del lenguaje que permite que una subclase proporcione una implementación especializada a un método que ya existe en la superclase. De esta forma, la subclase redefine la implementación de la superclase, proporcionando un método con la misma firma y tipo de retorno, que reemplaza el método de la clase padre. La firma o signatura es la combinación del nombre del método y la lista de parámetros de los métodos. Si definimos una variable vehículo del tipo Vehículo, esta puede hacer referencia a objetos de la clase Vehículo (no abstracta) o de alguna de sus subclases, por ejemplo, Auto o Moto. Vehiculo vehículo = new Vehiculo(); Vehiculo auto = new Auto(); Vehiculo moto = new Moto(); Aunque declaramos todas las variables del tipo Vehículo, las variables auto y moto hacen referencia a una instancia de la clase Auto y Moto, respectivamente. En la declaración de la variable, utilizamos un tipo de clase, pero, cuando se crea efectivamente utilizando el operador new, la variable puede apuntar a cualquier instancia de una subclase. Esta propiedad se conoce como polimorfismo. En Java, las variables referencia de una clase son polimórficas, ya que pueden referirse a un objeto de su clase o a uno de alguna de sus subclases, y el método invocado es el definido en la clase de la referencia polimórfica. Vamos a analizar, ahora, un ejemplo que nos permita validar estos conceptos. Clase Vehículo package vehiculo; public class Vehiculo { private String color; public Vehiculo(String color) { this.color = color; } public String mostrarDatos (){ return ("Color: " + color); } } Subclase Moto package vehiculo; public class Moto extends Vehiculo{ private int ruedas; public Moto(int ruedas, String color) { super(color); this.ruedas = ruedas; } @Override public String mostrarDatos (){ return ("Moto\n"+super.mostrarDatos()+" Ruedas: "+ ruedas); } } Subclase Auto package vehiculo; public class Auto extends Vehiculo{ private int ruedas; public Auto(int ruedas, String color) { super(color); this.ruedas = ruedas; } @Override public String mostrarDatos (){ return ("Auto\n"+super.mostrarDatos()+" Ruedas: "+ ruedas); } } Clase principal Menú package vehiculo; public class Menu { public static void main(String[] args) { Vehiculo auto= new Auto(4,"Rojo"); System.out.println(auto.mostrarDatos()); Vehiculo moto=new Moto(2,"Verde"); System.out.println(moto.mostrarDatos()); } } Figura 1: Salida Fuente: elaboración propia. Siguiendo con el ejemplo que traemos, vimos que podemos definir las variables auto y moto del tipo Vehículo. Se suele usar para estos casos la sobreescritura del método toString, pero en nuestro caso utilizaremos el método mostrarDatos() de la clase Vehículo, sobrescrito en las clases Auto y Moto. En el caso de la variable auto, cuando hemos invocado al método mostrarDatos(), el método ejecutado es el que se encuentra en la clase Auto porque es el objeto creado y al que se hace referencia es una instancia de esta clase. La misma lógica se aplica para la variable moto. Esta variable es de tipo Vehículo, pero hace referencia a una instancia de la clase Moto. En consecuencia, el método mostrarDatos() a ejecutar será de esta clase. Casting en variables polimórficas Cuando utilizamos variables con referencias polimórficas, solamente podemos invocar aquellos métodos definidos en la clase base a la que pertenece la variable. Por ejemplo, para la siguiente declaración: Vehículo moto = new Moto(); La variable moto está definida del tipo Vehículo, por lo que solo podrá invocar métodos definidos en esta clase. Aunque la variable moto esté referenciando a una instancia de la clase Moto, no podrá invocar los métodos definidos en esta última clase, ya que dará un error de compilación. Lo mostramos a continuación. Método en la clase Moto public int getRuedas() { return ruedas; } Al intentar invocarlo desde la variable moto definida del tipo Vehículo: Vehiculo moto=new Moto(2,"Verde"); System.out.println(moto.getRuedas()); Aparece un error de compilación: Figura 2: Error de compilación Fuente: elaboración propia. En tiempo de compilación, se debe invocar a los métodos definidos en la clase base de la variable. Esto se debe a que una variable de tipo Vehículo puede referenciar a instancias de las subclases Moto o Auto, y puede haber métodos que están definidos en la clase Auto, pero no en la clase Moto y viceversa. Es decir que con el polimorfismo una variable de una clase puede hacer referencia a una subclase, pero, cuando se invocan los métodos sobre esta variable, solamente pueden invocarse los métodos definidos en la clase base. En tiempo de ejecución, el método invocado será el que está definido en la subclase a la que hace referencia. Para poder invocar a los métodos definidos en la clase de la instancia, se puede realizar un casting a la clase polimórfica. Moto m = (Moto) moto; m.getRuedas() Mediante el empleo de casting, indicamos que la variable moto se trata de una instancia de la clase Moto. Reescribiendo el menú principal podremos verlo con más claridad. package vehiculo; public class Menu { public static void main(String[] args) { Vehiculo auto= new Auto(4,"Rojo"); System.out.println(auto.mostrarDatos()); Vehiculo moto=new Moto(2,"Verde"); System.out.println(moto.mostrarDatos()); Moto m = (Moto) moto; System.out.println("Método getRuedas Moto: "+m.getRuedas()); } } Salida: Figura 3: Salida Fuente: elaboración propia. Si la variable moto no hiciera referencia a una instancia de la clase Moto, obtendríamos un error en tiempo de ejecución (ClassCastException). Una forma de saber la clase a la que pertenece la instancia referida por una variable es mediante el operador instanceof que vimos en el módulo 1. if (moto instanceof Moto) { System.out.println("La variable moto es instancia de Moto"); } Ventajas del polimorfismo El polimorfismo en Java permite la reutilización de código donde la subclase proporciona una implementación especializada a un método que ya existe en la superclase. Otorga flexibilidad y uniformidad a nuestros desarrollos mediante la sobreescritura de métodos, sin perder ninguna de las ventajas de la compilación estática. Simplifica la programación, ya que “… nos permite escribir programas que procesen objetos que compartan la misma superclase (ya sea de manera directa o indirecta) como si todos fueran objetos de la superclase” (Argentina Programa, s.f., https://bit.ly/35p4uMs). Métodos no derivables: atributo final “En el contexto de herencia, la palabra reservada final se emplea para proteger la redefinición de los métodos de la clase base” (Martín Velasco, 2019, https://bit.ly/3IRpMjw). Un método con dicho atributo no puede volver a definirse en las clases derivadas, por ejemplo: public class Persona { final public int getEdad() {... } } La clase Empleado que derive de Persona, hereda el método getEdad(), pero no puede cambiar su definición; un método declarado final no se puede redefinir o anular en las clases derivadas. Tipos de polimorfismo En p

Use Quizgecko on...
Browser
Browser