Simulador EGEL Ciencias Computacionales

⌨️ Lenguajes de programación

Lenguajes de programación: paradigmas y traza de código

El EGEL Plus Ciencias Computacionales clasifica los lenguajes por paradigma. Los cuatro paradigmas fundamentales son el imperativo (procedimental), el orientado a objetos (herencia, polimorfismo y encapsulamiento), el funcional y el lógico. Los reactivos entregan el código en un bloque de texto y exigen identificar el paradigma y trazar el comportamiento paso a paso.

Al trazar expresiones aritméticas, los operadores multiplicativos (* / %) tienen mayor precedencia que los aditivos (+ -). En C y Java la división entre enteros es entera y trunca hacia cero (7/2 = 3 y -7/2 = -3), y el residuo % toma el signo del dividendo (-7 % 3 = -1). En Python el comportamiento difiere: % toma el signo del divisor (-7 % 3 = 2), // (división entera) redondea hacia menos infinito (-7 // 2 = -4) y / siempre devuelve float en Python 3 (7 / 2 = 3.5). La aritmética double sigue IEEE 754 (64 bits), por eso 0.1 + 0.2 = 0.30000000000000004.

En ámbito y paso de parámetros, Java es siempre paso por valor: con objetos se copia el valor de la referencia, así el método puede mutar el objeto apuntado pero no reasignar la referencia original del llamador.

Para los paradigmas no imperativos: el funcional puro se caracteriza por la transparencia referencial y la ausencia de efectos secundarios (misma entrada, mismo resultado, sustituible por su valor). El lógico, representado por Prolog, se basa en la unificación y el retroceso (backtracking) para resolver consultas de forma declarativa a partir de hechos y reglas.

Practica el banco completo y haz simulacros gratis

Preguntas de muestra (35)

1. En C, C++, Java y Python, al evaluar una expresión aritmética sin paréntesis, ¿qué jerarquía de operadores aplican el compilador o el intérprete?

  1. Los operadores multiplicativos (*, /, %) se evalúan antes que los operadores aditivos (+, -).
  2. Los operadores aditivos (+, -) se evalúan antes que los multiplicativos (*, /, %).
  3. Todos los operadores aritméticos tienen la misma precedencia y se evalúan estrictamente de izquierda a derecha.
  4. El operador de residuo (%) se evalúa siempre al final, después de los operadores aditivos.

Según ISO/IEC 9899 y la especificación de Java, los operadores * / % tienen mayor precedencia que + -, por lo que se resuelven primero al no haber paréntesis; asignar la misma precedencia a todos es incorrecto. (ISO/IEC 9899:2018 (C) §6.5; Java Language Specification, Java SE, §15.17-15.18)

2. Se ejecuta la siguiente instrucción en un lenguaje imperativo como C o Java: resultado = 10 - 4 * 2 + 6 / 2; ¿qué valor almacena la variable resultado al aplicar la precedencia estándar de operadores?

  1. 9
  2. 5
  3. 4
  4. -1

Aplicando la precedencia estándar, 4*2=8 y 6/2=3, por lo que 10-8+3=5; las demás opciones surgen de ignorar la precedencia o de agrupar mal los operadores + y -. (ISO/IEC 9899:2018 (C) §6.5; Java Language Specification §15.17-15.18)

3. En Java se ejecuta la instrucción int x = -7 / 2; Considerando que la división entre enteros trunca hacia cero, ¿qué valor almacena x?

  1. -4
  2. 3
  3. -3
  4. -3.5

En Java y C la división entera trunca hacia cero, por lo que -7/2 = -3 (no -4, que sería el resultado de redondear hacia menos infinito como en Python). (Java Language Specification §15.17.2; ISO/IEC 9899:2018 §6.5.5)

4. Un programador migra a Python un fragmento de código escrito originalmente en Java sin modificar la expresión -7 % 3, que en Java produce -1. Al ejecutar la misma expresión en Python 3, ¿qué valor se obtiene y por qué difiere del resultado en Java?

  1. -1
  2. 1
  3. -2
  4. 2

El operador % en Python toma el signo del divisor, por lo que -7 % 3 = 2, a diferencia de Java, donde el mismo operador toma el signo del dividendo y da -1. (The Python Language Reference §6.7 'Binary arithmetic operations'; Java Language Specification §15.17.3)

5. En Python 3 se ejecuta la instrucción print(-7 // 2). Considerando el comportamiento del operador de división entera //, ¿qué se imprime?

  1. -4
  2. -3
  3. 4
  4. -3.5

El operador // en Python redondea hacia menos infinito (piso), por lo que -7 // 2 = -4, y no -3 como ocurriría con un truncamiento hacia cero. (The Python Language Reference §6.7 'Binary arithmetic operations')

6. En Python 3 se ejecuta la instrucción print(7 / 2). ¿Qué valor se imprime, considerando que el operador / realiza siempre división de punto flotante?

  1. 3
  2. 3.5
  3. 3.0
  4. 4

En Python 3 el operador / siempre realiza división de punto flotante (true division), incluso entre dos enteros, por lo que 7/2 = 3.5 (no 3, que correspondería a la división entera con //). (The Python Language Reference §6.7; PEP 238)

7. En Java se evalúa la condición if (lista != null && lista.size() > 0) sobre una variable lista cuyo valor es null. Debido a la evaluación de cortocircuito del operador &&, ¿qué ocurre al ejecutar esta condición?

  1. Java evalúa ambos operandos siempre, por lo que se lanza una excepción NullPointerException al ejecutar lista.size().
  2. El operador && evalúa primero lista.size() > 0, por lo que la excepción ocurre antes de comprobar si lista es nula.
  3. El operador && detiene la evaluación después de comprobar que lista != null es falso, y nunca invoca lista.size(), evitando la excepción.
  4. La expresión completa se evalúa en tiempo de compilación y produce un error de compilación por referencia nula.

El operador && usa evaluación de cortocircuito y se detiene en el primer operando falso, por lo que nunca evalúa lista.size() si lista es null, evitando la excepción. (Java Language Specification §15.23-15.24; ISO/IEC 9899:2018 §6.5.13-6.5.14)

8. En Java un programador declara double a = 0.1, b = 0.2; y evalúa la condición if (a + b == 0.3). Considerando la representación en punto flotante IEEE 754 del tipo double, ¿cuál es el resultado de esta comparación y por qué?

  1. La condición es true, porque los valores double representan de forma exacta cualquier número decimal finito.
  2. La condición es false, porque Java trunca automáticamente los valores double a la centésima más cercana antes de comparar.
  3. La condición es true, porque el compilador redondea el resultado de la suma a 0.3 antes de ejecutar la comparación.
  4. La condición es false, porque la suma produce 0.30000000000000004, ya que 0.1 y 0.2 no tienen una representación binaria exacta.

Los valores double siguen IEEE 754 y no representan de forma exacta decimales como 0.1 o 0.2, por lo que su suma da 0.30000000000000004 y la comparación con 0.3 resulta false. (IEEE 754-2019 (Standard for Floating-Point Arithmetic); Java Language Specification §4.2.3)

9. En Java se ejecuta la instrucción System.out.println(1 + 2 + "x"); Considerando que el operador + es asociativo por la izquierda y que uno de los operandos finales es de tipo String, ¿qué se imprime en la consola?

  1. 3x
  2. x12
  3. 1x2
  4. 12x

El operador + es asociativo por la izquierda, así que 1+2 se suma aritméticamente antes de concatenarse con la cadena, dando como resultado 3x. (Java Language Specification §15.18.1)

10. Se ejecuta el siguiente fragmento en Java: int i = 5; int a = i++; int b = ++i; Al finalizar, ¿cuáles son los valores de a, b e i?

  1. a = 6, b = 7, i = 7
  2. a = 5, b = 7, i = 7
  3. a = 5, b = 6, i = 6
  4. a = 5, b = 7, i = 6

El postfijo (i++) devuelve el valor antes de incrementar (a=5) y el prefijo (++i) devuelve el valor después de incrementar (b=7), quedando i=7 al final. (Java Language Specification §15.14.2 (posfijo) y §15.15.1 (prefijo))

11. En C se declara int arreglo[8]; y se recorre con el ciclo for (int i = 0; i <= 8; i++) { arreglo[i] = i; } ¿Qué error de gestión de índices contiene este ciclo al trazar su ejecución?

  1. El ciclo es correcto, porque el índice máximo permitido siempre es igual al tamaño declarado del arreglo.
  2. El ciclo nunca termina, porque la condición i <= 8 genera un bucle infinito al no poder alcanzarse el valor 8.
  3. Cuando i vale 8, la instrucción accede a arreglo[8], un índice fuera del rango válido (0 a 7), lo que produce un acceso a memoria indebido.
  4. El error está en iniciar el ciclo en i = 0 en lugar de i = 1, ya que los arreglos en C inician en el índice 1.

Los índices válidos de un arreglo de 8 elementos van de 0 a 7; al permitir i=8 el ciclo accede a arreglo[8], que está fuera de rango. (ISO/IEC 9899:2018 (C) §6.5.2.1 (subíndices de arreglo); Java Language Specification §10.4 (array access))

12. El paradigma lógico, representado por lenguajes como Prolog, resuelve las consultas a partir de una base de hechos y reglas mediante dos mecanismos fundamentales. ¿Cuáles son?

  1. Herencia de clases y polimorfismo entre objetos.
  2. Asignación de variables y ciclos de control secuenciales.
  3. Recursión de cola y funciones puras sin efectos secundarios.
  4. Unificación de términos y retroceso (backtracking).

El paradigma lógico se basa en la unificación de términos y el retroceso (backtracking), a diferencia de los mecanismos propios de los paradigmas orientado a objetos, imperativo o funcional. (ISO/IEC 13211-1:1995 (Prolog); Sebesta, R., Concepts of Programming Languages (cap. Logic Programming))

13. Considera la siguiente base de conocimiento en Prolog: padre(juan, maria). padre(juan, pedro). padre(pedro, ana). abuelo(X, Y) :- padre(X, Z), padre(Z, Y). Al ejecutar la consulta ?- abuelo(juan, ana)., ¿qué ocurre al trazar la unificación y el posible retroceso?

  1. La consulta tiene éxito (true): Prolog unifica Z con maria, falla padre(maria, ana), retrocede y unifica Z con pedro, con lo cual padre(pedro, ana) se cumple.
  2. La consulta falla (false), porque no existe un hecho abuelo(juan, ana) declarado directamente en la base de conocimiento.
  3. La consulta tiene éxito (true), pero únicamente mediante la primera unificación Z = maria, sin que sea necesario ningún retroceso.
  4. Se genera un error de ejecución, porque las variables X, Y y Z deben declararse explícitamente antes de usarse en la regla.

Prolog intenta unificar Z con maria, falla al comprobar padre(maria, ana), retrocede y prueba Z=pedro, con lo que la consulta tiene éxito gracias al backtracking. (ISO/IEC 13211-1:1995 (Prolog), mecanismo de unificación y retroceso (backtracking))

14. En Prolog, cuando una submeta dentro del cuerpo de una regla falla durante la resolución de una consulta, ¿qué hace el motor de inferencia antes de declarar el fallo de la consulta completa?

  1. Detiene la ejecución de inmediato y reporta un error de sintaxis en el programa.
  2. Retrocede (backtracking) hacia la submeta anterior para intentar una unificación alternativa.
  3. Reinicia la base de conocimiento completa desde el primer hecho declarado.
  4. Ignora la submeta fallida y continúa evaluando la siguiente submeta sin retroceder.

Cuando una submeta falla, el motor de inferencia de Prolog retrocede (backtracking) hacia la submeta anterior para probar una unificación alternativa antes de declarar el fallo total. (ISO/IEC 13211-1:1995 (Prolog); Sebesta, R., Concepts of Programming Languages (cap. Logic Programming))

15. En un programa Prolog, una cláusula como padre(juan, maria)., escrita sin el operador :- y sin cuerpo, se denomina:

  1. Una regla (rule).
  2. Una consulta (query).
  3. Un hecho (fact).
  4. Una unificación (unification).

Una cláusula sin cuerpo que simplemente afirma una relación, como padre(juan, maria)., se denomina hecho en Prolog; una regla, en cambio, incluye el operador :- y un cuerpo. (ISO/IEC 13211-1:1995 (Prolog); Sebesta, R., Concepts of Programming Languages)

16. Considera las siguientes cláusulas en Prolog: mayor(X, Y) :- X > Y, !. mayor(X, Y) :- X =< Y. Al ejecutar la consulta ?- mayor(3, 5)., ¿qué ocurre al trazar su resolución?

  1. Falla por completo, porque el corte (!) de la primera cláusula impide que Prolog intente la segunda cláusula.
  2. Tiene éxito mediante la primera cláusula, porque el corte (!) obliga a aceptar la primera unificación posible sin evaluar la condición X > Y.
  3. Se produce un ciclo infinito, porque ambas cláusulas coinciden con los mismos argumentos X y Y.
  4. Tiene éxito mediante la segunda cláusula, porque en la primera la condición 3 > 5 falla antes del corte (!), y Prolog prueba la siguiente.

El corte (!) solo impide el retroceso una vez alcanzado; como 3 > 5 falla antes de llegar al corte, este nunca se ejecuta, Prolog prueba la segunda cláusula y la consulta tiene éxito. (ISO/IEC 13211-1:1995 (Prolog), operador de corte (cut))

17. Considera la siguiente definición en Prolog: suma_lista([], 0). suma_lista([H|T], S) :- suma_lista(T, S1), S is H + S1. Al ejecutar la consulta ?- suma_lista([3, 4, 5], S)., ¿qué valor obtiene S al trazar la recursión hasta el caso base y su posterior desenrollado?

  1. 12
  2. 9
  3. 0
  4. 15

La recursión llega al caso base suma_lista([],0) y se desenrolla sumando cada cabeza: 5+0=5, 4+5=9, 3+9=12, por lo que S=12. (ISO/IEC 13211-1:1995 (Prolog), recursión y unificación de listas)

18. En Prolog, si un hecho no aparece en ninguna parte de la base de conocimiento, una consulta ?- not(hecho_no_declarado). tiene éxito (true). ¿Qué principio explica este comportamiento?

  1. La herencia de clases, que asigna un valor por defecto cuando no existe un dato explícito.
  2. La negación por fallo (negation as failure): lo que no puede demostrarse verdadero a partir de los hechos y reglas se asume falso.
  3. La recolección de basura, que elimina de la memoria los hechos no utilizados por el programa.
  4. La sobrecarga de operadores, que reinterpreta not como una función aritmética sobre enteros.

La negación por fallo establece que si un hecho no puede probarse verdadero con la base de conocimiento disponible, se asume falso, por lo que la consulta tiene éxito. (ISO/IEC 13211-1:1995 (Prolog), negación por fallo (negation as failure))

19. A diferencia del paradigma imperativo, donde el programador especifica una secuencia de instrucciones y asignaciones para modificar el estado, el paradigma lógico exige que el programador especifique:

  1. Una secuencia detallada de asignaciones de variables y estructuras de control como ciclos.
  2. Clases y objetos que encapsulan datos y comportamiento mediante herencia.
  3. Hechos y reglas que describen relaciones, dejando que el motor de inferencia determine cómo obtener la solución.
  4. Funciones puras compuestas mediante aplicación y recursión, sin estado mutable.

El paradigma lógico exige declarar hechos y reglas sobre relaciones, dejando que el motor de inferencia determine el procedimiento de solución, a diferencia de los demás paradigmas. (CENEVAL, Guía para el sustentante EGEL Plus Ciencias Computacionales (área Lenguajes de programación); Sebesta, R., Concepts of Programming Languages)

20. En C, p es un apuntador que almacena la dirección de una variable entera x. ¿Qué operador se utiliza para acceder o modificar el valor de x a través de p?

  1. El operador de dirección &, como en la instrucción &p = 10;
  2. El operador de punto ., como en la instrucción p.x = 10;
  3. El operador de flecha ->, ya que * únicamente sirve para declarar apuntadores.
  4. El operador de desreferencia *, como en la instrucción *p = 10;

En C, el operador de desreferencia * permite acceder o modificar el valor almacenado en la dirección que contiene un apuntador, como en *p = 10;; el operador & obtiene la dirección, no el valor. (ISO/IEC 9899:2018 (C) §6.5.3.2 (operadores de apuntadores * y &))

21. Se ejecuta el siguiente código en C: void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main() { int x = 3, y = 7; swap(&x, &y); } Al finalizar la llamada swap(&x, &y);, ¿cuáles son los valores de x e y?

  1. x = 7, y = 3, porque los apuntadores permiten modificar directamente las variables originales a través de sus direcciones de memoria.
  2. x = 3, y = 7, sin cambio, porque C siempre pasa los argumentos de una función por valor y nunca permite modificar las variables originales.
  3. x = 7, y = 7, porque ambas variables terminan tomando el último valor asignado a la variable temp.
  4. Se produce un error de compilación, porque no se puede desreferenciar un apuntador dentro del cuerpo de una función.

Al pasar las direcciones &x y &y, la función swap modifica directamente las variables originales a través de los apuntadores, intercambiando sus valores a 7 y 3. (ISO/IEC 9899:2018 (C) §6.5.2.2 y §6.5.3.2 (paso de argumentos y desreferencia de apuntadores))

22. Se ejecuta el siguiente código en Java: void modificar(int[] arr) { arr[0] = 99; arr = new int[]{1, 2, 3}; } int[] datos = {5, 6, 7}; modificar(datos); Después de llamar a modificar(datos);, ¿qué valor tiene datos[0]?

  1. 5, porque Java pasa los arreglos por valor completo, copiando todos sus elementos al invocar el método.
  2. 99, porque el método muta el arreglo mediante la referencia copiada, pero reasignar arr no afecta la referencia datos del llamador.
  3. 1, porque la reasignación de arr dentro del método sustituye también al arreglo datos del llamador.
  4. Error en tiempo de ejecución, porque un método no puede modificar el contenido de un arreglo recibido como parámetro.

Java pasa los objetos por valor de referencia: el método puede mutar el arreglo original (arr[0]=99), pero reasignar arr a un nuevo arreglo solo cambia la copia local de la referencia, sin afectar a datos. (Java Language Specification §8.4.1 y §15.12.4.4 (invocación de método))

23. En C, ¿qué función de la biblioteca estándar reserva un bloque de memoria dinámica en el montículo (heap) durante la ejecución del programa?

  1. free()
  2. atoi()
  3. malloc()
  4. sizeof()

malloc() es la función de la biblioteca estándar de C que reserva un bloque de memoria dinámica en el heap; free() libera memoria y sizeof es un operador que calcula tamaños en tiempo de compilación. (ISO/IEC 9899:2018 (C) §7.22.3 (stdlib.h, gestión de memoria dinámica))

24. Un programa en C ejecuta int *p = malloc(sizeof(int) * 100);, utiliza el bloque reservado y después la variable p sale de su alcance (scope) sin que se haya llamado a free(p). ¿Qué problema de gestión de memoria se produce?

  1. Un apuntador colgante (dangling pointer), porque p sigue apuntando a una zona de memoria ya liberada.
  2. Un desbordamiento de búfer (buffer overflow), porque se reservó más memoria de la que el programa necesitaba.
  3. Una doble liberación (double free), porque free() se invocará automáticamente dos veces al finalizar el programa.
  4. Una fuga de memoria (memory leak), porque el bloque reservado en el heap queda inaccesible pero nunca se libera.

Cuando el apuntador que referencia un bloque reservado con malloc sale de alcance sin llamar a free(), ese bloque queda inaccesible pero reservado, lo que constituye una fuga de memoria; no es un apuntador colgante, pues la memoria nunca se liberó. (ISO/IEC 9899:2018 (C) §7.22.3 (gestión de memoria dinámica malloc/free))

25. Se ejecuta el siguiente código en C: int *p = malloc(sizeof(int)); *p = 5; free(p); printf("%d", *p); ¿Qué problema de gestión de memoria contiene la instrucción printf al trazar su ejecución?

  1. Se desreferencia un apuntador colgante: la memoria de p ya fue liberada por free(), por lo que acceder a *p produce un comportamiento indefinido.
  2. El programa funciona correctamente, porque free() solo marca la memoria como reutilizable sin borrar el valor almacenado.
  3. Se produce una fuga de memoria, porque free() no elimina el valor almacenado previamente en p.
  4. Se produce un desbordamiento de pila, porque malloc reserva la memoria en la pila y no en el montículo.

Acceder a *p después de free(p) desreferencia un apuntador colgante, ya que la memoria fue liberada y su contenido y comportamiento quedan indefinidos; no es una fuga, pues la memoria sí se liberó. (ISO/IEC 9899:2018 (C) §7.22.3.3 (comportamiento indefinido tras free))

26. A diferencia de C, donde el programador debe liberar explícitamente la memoria dinámica con free(), en Java la memoria ocupada por objetos sin referencias activas se libera mediante:

  1. La llamada explícita al método delete() sobre cada objeto antes de finalizar el programa.
  2. El recolector de basura (garbage collector), un proceso automático de la máquina virtual de Java.
  3. El operador free, heredado del lenguaje C y disponible también en Java.
  4. El sistema operativo, que libera la memoria completa del proceso después de cada asignación individual.

A diferencia de C, donde free() libera la memoria manualmente, en Java el recolector de basura libera automáticamente la memoria de los objetos sin referencias activas; Java no tiene delete() ni el operador free. (Java Language Specification §12.6 (finalización) y JVM Specification (gestión automática de memoria))

27. En la programación orientada a objetos, el principio que consiste en ocultar los detalles internos de una clase y exponer su funcionalidad únicamente a través de métodos públicos definidos en su interfaz se conoce como:

  1. Encapsulamiento
  2. Herencia
  3. Polimorfismo
  4. Sobrecarga

El encapsulamiento oculta el estado interno de un objeto y restringe el acceso directo a sus atributos, exponiendo solo la interfaz pública; la herencia y el polimorfismo son principios distintos de la POO. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (encapsulation).)

28. Cuando una clase Perro se define a partir de una clase Animal y adquiere automáticamente sus atributos y métodos, pudiendo además agregar o modificar comportamiento propio, el mecanismo de la POO que se está aplicando es:

  1. Herencia
  2. Encapsulamiento
  3. Sobrecarga
  4. Acoplamiento

La herencia permite que una clase derivada reutilice y extienda los atributos y métodos de una clase base, estableciendo una relación es-un; el encapsulamiento y la sobrecarga son conceptos distintos. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (inheritance).)

29. Se tiene el siguiente bloque de código en Java: class Figura { public String nombre() { return "Figura"; } } class Circulo extends Figura { public String nombre() { return "Circulo"; } } public class Main { public static void main(String[] args) { Figura f = new Circulo(); System.out.println(f.nombre()); } } ¿Qué se imprime al ejecutar este programa y qué principio de la POO lo explica?

  1. Imprime Circulo, por polimorfismo con enlace dinámico según el tipo real del objeto
  2. Imprime Figura, por enlace estático según el tipo declarado de la referencia
  3. Imprime Circulo, por herencia simple, sin que exista sobrescritura de métodos
  4. Ocurre un error de compilación porque Figura no declara el método como abstracto

En Java el enlace de métodos de instancia es dinámico: la JVM invoca el método sobrescrito en la clase del objeto real (Circulo), no en el tipo declarado de la referencia (Figura), lo cual es la esencia del polimorfismo. (Java Language Specification §15.12.4 (invocación dinámica de métodos de instancia).)

30. Una clase define dos métodos con el mismo nombre calcular: uno recibe dos parámetros de tipo entero y el otro recibe un solo parámetro de tipo flotante; el compilador decide en tiempo de compilación cuál método invocar según la lista de parámetros. ¿Cómo se denomina este mecanismo?

  1. Sobrecarga de métodos
  2. Sobrescritura de métodos
  3. Herencia múltiple
  4. Enlace dinámico

La sobrecarga (overloading) permite varios métodos con el mismo nombre pero distinta firma, resuelta en tiempo de compilación; la sobrescritura (overriding) redefine un método heredado con la misma firma y se resuelve en tiempo de ejecución. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (overloading vs. overriding).)

31. Se tiene el siguiente bloque de código en Java: class Base { int valor = 10; public int obtener() { return valor; } } class Derivada extends Base { int valor = 20; public int obtener() { return valor; } } public class Main { public static void main(String[] args) { Base b = new Derivada(); System.out.println(b.valor + " " + b.obtener()); } } ¿Qué se imprime al ejecutar este programa?

  1. 10 20
  2. 20 20
  3. 10 10
  4. 20 10

Los campos se enlazan de forma estática según el tipo declarado de la referencia (Base), por lo que b.valor da 10, mientras que los métodos se enlazan dinámicamente según el objeto real (Derivada), por lo que b.obtener() da 20. (Java Language Specification §15.11 (acceso a campos resuelto por tipo estático) y §15.12.4 (invocación dinámica de métodos).)

32. En un lenguaje orientado a objetos como Java, un atributo declarado con el modificador de acceso private puede ser accedido directamente desde:

  1. Los métodos definidos dentro de la propia clase
  2. Cualquier clase que pertenezca al mismo paquete
  3. Cualquier subclase, sin importar el paquete al que pertenezca
  4. Cualquier clase del programa que tenga acceso al objeto

El modificador private restringe el acceso al atributo exclusivamente a los métodos de la propia clase, lo que refuerza el encapsulamiento; ni las subclases ni las clases del mismo paquete tienen acceso directo a miembros private. (Java Language Specification §6.6.1 (Determining Accessibility).)

33. Un equipo de desarrollo diseña una clase CuentaBancaria y quiere impedir que el saldo se modifique directamente desde fuera de la clase, permitiendo que se actualice solo mediante métodos depositar y retirar que validan reglas de negocio. ¿Qué práctica de diseño orientado a objetos deben aplicar?

  1. Declarar el saldo como atributo privado y exponer solo métodos de acceso controlado
  2. Declarar el saldo como atributo público y validar las reglas en la interfaz de usuario
  3. Heredar la clase de una superclase Numero para restringir su tipo
  4. Sobrecargar el operador de asignación para el atributo saldo

El caso describe encapsulamiento aplicado correctamente: el atributo se protege con un modificador privado y se manipula solo a través de métodos que garantizan las invariantes de la clase. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (encapsulation e information hiding).)

34. En un lenguaje de tipado estático como Java o C, la verificación de tipos de las variables y expresiones se realiza principalmente:

  1. En tiempo de compilación, antes de ejecutar el programa
  2. En tiempo de ejecución, cada vez que se usa una variable
  3. Solo cuando el programador invoca una función de verificación
  4. Durante la fase de enlace del programa, no antes de esa etapa

En los lenguajes de tipado estático, el compilador determina y verifica los tipos de las variables antes de la ejecución, lo que permite detectar errores de tipo en tiempo de compilación. (Sebesta, R., Concepts of Programming Languages, cap. Names, Bindings, and Type Checking.)

35. Se dice que un lenguaje de programación tiene un sistema de tipos fuerte cuando:

  1. No permite operaciones entre tipos incompatibles sin una conversión explícita
  2. Permite que cualquier valor se use como cualquier tipo sin restricción
  3. Determina los tipos de las variables en tiempo de ejecución, y no en tiempo de compilación
  4. Requiere que todas las variables se declaren con la palabra reservada var

El tipado fuerte impide o restringe estrictamente las operaciones entre tipos incompatibles, exigiendo conversiones explícitas o reglas de coerción bien definidas, a diferencia del tipado débil. (Sebesta, R., Concepts of Programming Languages, cap. Data Types (strong vs. weak typing).)

Comienza gratis