Ir al contenido principal

Operadores aritméticos, relacionales y lógicos

Introducción

Los operadores son símbolos especiales que realizan operaciones sobre uno, dos o más operandos (valores o variables). Son fundamentales en cualquier lenguaje de programación, ya que nos permiten manipular datos, realizar cálculos y tomar decisiones en nuestros programas. Java dispone de un conjunto completo de operadores que podemos clasificar en diferentes categorías según su función: aritméticos, relacionales, lógicos, de asignación, entre otros. En este artículo, exploraremos en detalle los operadores aritméticos, relacionales y lógicos, aprendiendo cómo utilizarlos correctamente en nuestros programas Java para realizar cálculos y tomar decisiones basadas en condiciones.

Operadores aritméticos

Los operadores aritméticos nos permiten realizar operaciones matemáticas básicas como suma, resta, multiplicación y división. Java proporciona los siguientes operadores aritméticos:

Operadores aritméticos básicos

Operador Descripción Ejemplo
+ Suma a + b
- Resta a - b
* Multiplicación a * b
/ División a / b
% Módulo (resto de la división) a % b

Veamos algunos ejemplos de cómo utilizar estos operadores:

public class OperadoresAritmeticos {
    public static void main(String[] args) {
        int a = 10;
        int b = 3;
        
        // Operaciones básicas
        int suma = a + b;
        int resta = a - b;
        int multiplicacion = a * b;
        int division = a / b;
        int modulo = a % b;
        
        System.out.println("Suma: " + suma);             // Resultado: 13
        System.out.println("Resta: " + resta);           // Resultado: 7
        System.out.println("Multiplicación: " + multiplicacion); // Resultado: 30
        System.out.println("División: " + division);     // Resultado: 3
        System.out.println("Módulo: " + modulo);         // Resultado: 1
    }
}

Es importante tener en cuenta algunas consideraciones especiales:

  1. División entre enteros: Cuando dividimos dos números enteros en Java, el resultado es otro número entero (se trunca la parte decimal). Si queremos obtener un resultado con decimales, al menos uno de los operandos debe ser de tipo float o double.
int a = 10;
int b = 3;
int divisionEntera = a / b;      // Resultado: 3
double divisionDecimal = a / (double)b;  // Resultado: 3.3333...

System.out.println("División entera: " + divisionEntera);
System.out.println("División decimal: " + divisionDecimal);
  1. El operador módulo %: Este operador devuelve el resto de la división entre dos números. Es muy útil para determinar si un número es divisible por otro (el resultado será 0) o para operaciones cíclicas.
// Comprobar si un número es par
int numero = 15;
boolean esPar = (numero % 2 == 0);
System.out.println(numero + " es par: " + esPar);  // Resultado: 15 es par: false

Operadores de incremento y decremento

Java proporciona operadores especiales para incrementar o decrementar una variable en una unidad:

Operador Descripción Ejemplo
++ Incremento a++ o ++a
-- Decremento a-- o --a

Estos operadores pueden utilizarse en dos formas:

  • Prefijo (++a, --a): Primero se incrementa/decrementa la variable y luego se utiliza su valor.
  • Sufijo (a++, a--): Primero se utiliza el valor actual de la variable y luego se incrementa/decrementa.

Veamos la diferencia con un ejemplo:

public class OperadoresIncrementoDecremento {
    public static void main(String[] args) {
        int a = 5;
        int b;
        
        // Incremento en sufijo
        b = a++;  // b = 5, después a se incrementa a 6
        System.out.println("Después de b = a++: a = " + a + ", b = " + b);
        
        // Reiniciamos a
        a = 5;
        
        // Incremento en prefijo
        b = ++a;  // a se incrementa a 6, después b = 6
        System.out.println("Después de b = ++a: a = " + a + ", b = " + b);
        
        // Reiniciamos a
        a = 5;
        
        // Decremento en sufijo
        b = a--;  // b = 5, después a se decrementa a 4
        System.out.println("Después de b = a--: a = " + a + ", b = " + b);
        
        // Reiniciamos a
        a = 5;
        
        // Decremento en prefijo
        b = --a;  // a se decrementa a 4, después b = 4
        System.out.println("Después de b = --a: a = " + a + ", b = " + b);
    }
}

Operadores aritméticos combinados con asignación

Java también proporciona operadores combinados que realizan una operación aritmética y una asignación en un solo paso:

Operador Equivalente Ejemplo
+= a = a + b a += b
-= a = a - b a -= b
*= a = a * b a *= b
/= a = a / b a /= b
%= a = a % b a %= b

Estos operadores son útiles para escribir código más conciso:

public class OperadoresCombinados {
    public static void main(String[] args) {
        int a = 10;
        
        // Operaciones combinadas
        a += 5;  // a = a + 5, ahora a = 15
        System.out.println("Después de a += 5: a = " + a);
        
        a -= 3;  // a = a - 3, ahora a = 12
        System.out.println("Después de a -= 3: a = " + a);
        
        a *= 2;  // a = a * 2, ahora a = 24
        System.out.println("Después de a *= 2: a = " + a);
        
        a /= 4;  // a = a / 4, ahora a = 6
        System.out.println("Después de a /= 4: a = " + a);
        
        a %= 4;  // a = a % 4, ahora a = 2
        System.out.println("Después de a %= 4: a = " + a);
    }
}

Operadores relacionales

Los operadores relacionales se utilizan para comparar dos valores y determinar la relación entre ellos. El resultado de una expresión relacional es siempre un valor booleano (true o false).

Operador Descripción Ejemplo
== Igual a a == b
!= No igual a a != b
> Mayor que a > b
< Menor que a < b
>= Mayor o igual que a >= b
<= Menor o igual que a <= b

Veamos cómo utilizar estos operadores:

public class OperadoresRelacionales {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        
        // Comparaciones
        boolean iguales = (a == b);
        boolean diferentes = (a != b);
        boolean mayor = (a > b);
        boolean menor = (a < b);
        boolean mayorIgual = (a >= b);
        boolean menorIgual = (a <= b);
        
        System.out.println("a == b: " + iguales);    // false
        System.out.println("a != b: " + diferentes); // true
        System.out.println("a > b: " + mayor);       // false
        System.out.println("a < b: " + menor);       // true
        System.out.println("a >= b: " + mayorIgual); // false
        System.out.println("a <= b: " + menorIgual); // true
        
        // Uso común en estructuras condicionales
        if (a < b) {
            System.out.println("a es menor que b");
        } else {
            System.out.println("a no es menor que b");
        }
    }
}

¡Importante! Para comparar objetos, especialmente cadenas (String), no debemos usar el operador == ya que compara referencias de memoria, no el contenido. En su lugar, debemos usar el método equals():

String cadena1 = "Hola";
String cadena2 = "Hola";
String cadena3 = new String("Hola");

// Comparación incorrecta para contenido de cadenas
boolean comparacion1 = (cadena1 == cadena2);  // Puede dar true por optimización del compilador
boolean comparacion2 = (cadena1 == cadena3);  // Normalmente dará false

// Comparación correcta para contenido
boolean comparacion3 = cadena1.equals(cadena2);  // true
boolean comparacion4 = cadena1.equals(cadena3);  // true

System.out.println("cadena1 == cadena2: " + comparacion1);
System.out.println("cadena1 == cadena3: " + comparacion2);
System.out.println("cadena1.equals(cadena2): " + comparacion3);
System.out.println("cadena1.equals(cadena3): " + comparacion4);

Operadores lógicos

Los operadores lógicos se utilizan para combinar expresiones booleanas y realizar operaciones de lógica booleana. Java proporciona los siguientes operadores lógicos:

Operador Descripción Ejemplo
&& AND lógico a && b
|| OR lógico a || b
! NOT lógico !a

Veamos cómo funcionan estos operadores:

Operador AND (&&)

El operador AND devuelve true solo si ambas expresiones son true:

a b a && b
true true true
true false false
false true false
false false false
boolean condicion1 = true;
boolean condicion2 = false;

boolean resultadoAnd = condicion1 && condicion2;
System.out.println("condicion1 && condicion2: " + resultadoAnd);  // false

// Ejemplo práctico: verificar si un número está en un rango
int numero = 15;
boolean estaEnRango = (numero >= 10) && (numero <= 20);
System.out.println("¿El número " + numero + " está entre 10 y 20? " + estaEnRango);  // true

Operador OR (||)

El operador OR devuelve true si al menos una de las expresiones es true:

a b a || b
true true true
true false true
false true true
false false false
boolean condicion1 = true;
boolean condicion2 = false;

boolean resultadoOr = condicion1 || condicion2;
System.out.println("condicion1 || condicion2: " + resultadoOr);  // true

// Ejemplo práctico: verificar si un número está fuera de un rango
int numero = 5;
boolean estaFueraDeRango = (numero < 10) || (numero > 20);
System.out.println("¿El número " + numero + " está fuera del rango 10-20? " + estaFueraDeRango);  // true

Operador NOT (!)

El operador NOT invierte el valor de una expresión booleana:

a !a
true false
false true
boolean condicion = true;

boolean resultadoNot = !condicion;
System.out.println("!condicion: " + resultadoNot);  // false

// Ejemplo práctico: verificar si un número no es par
int numero = 15;
boolean noPar = !(numero % 2 == 0);
System.out.println("¿El número " + numero + " no es par? " + noPar);  // true

Evaluación de cortocircuito

Java utiliza la evaluación de cortocircuito para los operadores && y ||. Esto significa:

  • Para &&: Si la primera expresión es false, la segunda expresión no se evalúa, ya que el resultado final será false independientemente.
  • Para ||: Si la primera expresión es true, la segunda expresión no se evalúa, ya que el resultado final será true independientemente.

Esto puede ser útil para evitar errores, como en el siguiente ejemplo:

public class EvaluacionCortocircuito {
    public static void main(String[] args) {
        int divisor = 0;
        int numerador = 10;
        
        // Sin cortocircuito, esto causaría una excepción de división por cero
        // if (divisor != 0 & numerador / divisor > 2) { ... }
        
        // Con cortocircuito, la segunda parte no se evalúa si divisor es 0
        if (divisor != 0 && numerador / divisor > 2) {
            System.out.println("La condición se cumple");
        } else {
            System.out.println("La condición no se cumple o el divisor es 0");
        }
    }
}

Operadores lógicos de bits

Java también proporciona operadores lógicos a nivel de bits, que operan sobre los bits individuales de valores enteros:

Operador Descripción Ejemplo
& AND a nivel de bits a & b
| OR a nivel de bits a | b
^ XOR a nivel de bits a ^ b
~ NOT a nivel de bits ~a
<< Desplazamiento a la izquierda a << b
>> Desplazamiento a la derecha con signo a >> b
>>> Desplazamiento a la derecha sin signo a >>> b

Estos operadores son utilizados principalmente en programación de bajo nivel, manipulación de bits y situaciones donde se requiere optimización de memoria.

public class OperadoresBits {
    public static void main(String[] args) {
        int a = 5;  // En binario: 0101
        int b = 3;  // En binario: 0011
        
        // Operaciones a nivel de bits
        int resultadoAnd = a & b;   // 0101 & 0011 = 0001 (1 en decimal)
        int resultadoOr = a | b;    // 0101 | 0011 = 0111 (7 en decimal)
        int resultadoXor = a ^ b;   // 0101 ^ 0011 = 0110 (6 en decimal)
        int resultadoNot = ~a;      // ~0101 = 1010 con extensión de signo (-6 en decimal con complemento a 2)
        
        System.out.println("a & b: " + resultadoAnd);
        System.out.println("a | b: " + resultadoOr);
        System.out.println("a ^ b: " + resultadoXor);
        System.out.println("~a: " + resultadoNot);
        
        // Desplazamientos
        int desplazamientoIzq = a << 1;  // 0101 << 1 = 1010 (10 en decimal)
        int desplazamientoDer = a >> 1;  // 0101 >> 1 = 0010 (2 en decimal)
        
        System.out.println("a << 1: " + desplazamientoIzq);
        System.out.println("a >> 1: " + desplazamientoDer);
    }
}

Operador ternario

Java proporciona un operador ternario (también conocido como operador condicional), que es una forma abreviada de una instrucción if-else:

expresión_condicional ? expresión_si_verdadero : expresión_si_falso

Este operador evalúa la expresión_condicional; si es true, el resultado es expresión_si_verdadero, si es false, el resultado es expresión_si_falso.

public class OperadorTernario {
    public static void main(String[] args) {
        int edad = 17;
        
        // Usando if-else
        String mensaje;
        if (edad >= 18) {
            mensaje = "Eres mayor de edad";
        } else {
            mensaje = "Eres menor de edad";
        }
        System.out.println(mensaje);
        
        // Equivalente usando operador ternario
        mensaje = (edad >= 18) ? "Eres mayor de edad" : "Eres menor de edad";
        System.out.println(mensaje);
        
        // También se puede usar directamente en una expresión
        System.out.println((edad >= 18) ? "Eres mayor de edad" : "Eres menor de edad");
        
        // Ejemplo con asignación de valores
        int descuento = (edad < 12) ? 50 : (edad >= 65) ? 40 : 10;
        System.out.println("Tu descuento es del " + descuento + "%");
    }
}

Precedencia de operadores

Cuando utilizamos múltiples operadores en una expresión, es importante entender el orden en que Java los evalúa. A continuación, se muestra la precedencia de los operadores en Java, de mayor a menor:

  1. Operadores postfijos: expr++, expr--
  2. Operadores unarios: ++expr, --expr, +expr, -expr, ~, !
  3. Multiplicación, división, módulo: *, /, %
  4. Suma, resta: +, -
  5. Desplazamiento: <<, >>, >>>
  6. Relacionales: <, >, <=, >=, instanceof
  7. Igualdad: ==, !=
  8. AND a nivel de bits: &
  9. XOR a nivel de bits: ^
  10. OR a nivel de bits: |
  11. AND lógico: &&
  12. OR lógico: ||
  13. Operador ternario: ? :
  14. Asignación: =, +=, -=, *=, /=, %=, etc.

Es recomendable usar paréntesis para hacer que el código sea más legible y para evitar confusiones:

public class PrecedenciaOperadores {
    public static void main(String[] args) {
        int a = 5, b = 3, c = 8;
        
        // Sin paréntesis, la multiplicación tiene precedencia sobre la suma
        int resultado1 = a + b * c;  // 5 + (3 * 8) = 5 + 24 = 29
        
        // Con paréntesis, cambiamos la precedencia
        int resultado2 = (a + b) * c;  // (5 + 3) * 8 = 8 * 8 = 64
        
        System.out.println("a + b * c = " + resultado1);
        System.out.println("(a + b) * c = " + resultado2);
        
        // Ejemplo con operadores lógicos
        boolean res1 = a > b && b < c;  // (a > b) && (b < c) = true && true = true
        boolean res2 = a > b || b > c;  // (a > b) || (b > c) = true || false = true
        
        System.out.println("a > b && b < c: " + res1);
        System.out.println("a > b || b > c: " + res2);
    }
}

Ejemplos prácticos

Ejemplo 1: Calculadora simple

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Calculadora Simple");
        System.out.println("------------------");
        System.out.print("Introduce el primer número: ");
        double num1 = scanner.nextDouble();
        
        System.out.print("Introduce el segundo número: ");
        double num2 = scanner.nextDouble();
        
        System.out.println("Suma: " + (num1 + num2));
        System.out.println("Resta: " + (num1 - num2));
        System.out.println("Multiplicación: " + (num1 * num2));
        
        // Evitamos la división por cero
        if (num2 != 0) {
            System.out.println("División: " + (num1 / num2));
            System.out.println("Módulo: " + (num1 % num2));
        } else {
            System.out.println("No se puede dividir entre cero");
        }
        
        scanner.close();
    }
}

Ejemplo 2: Verificación de requisitos para un préstamo

import java.util.Scanner;

public class VerificacionPrestamo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Sistema de verificación de préstamos");
        System.out.println("------------------------------------");
        
        System.out.print("¿Cuál es tu edad? ");
        int edad = scanner.nextInt();
        
        System.out.print("¿Cuál es tu salario mensual? ");
        double salario = scanner.nextDouble();
        
        System.out.print("¿Cuántos años llevas en tu empleo actual? ");
        int antiguedad = scanner.nextInt();
        
        // Verificamos requisitos
        boolean edadSuficiente = edad >= 18;
        boolean salarioSuficiente = salario >= 1000;
        boolean antiguedadSuficiente = antiguedad >= 1;
        
        // Utilizamos operadores lógicos para combinar condiciones
        boolean cumpleRequisitos = edadSuficiente && (salarioSuficiente || antiguedadSuficiente);
        
        // Operador ternario para mensaje
        String mensaje = cumpleRequisitos 
            ? "¡Felicidades! Cumples los requisitos para solicitar un préstamo."
            : "Lo sentimos, no cumples los requisitos para un préstamo.";
        
        System.out.println("\nResultado de la verificación:");
        System.out.println("- Edad adecuada: " + edadSuficiente);
        System.out.println("- Salario suficiente: " + salarioSuficiente);
        System.out.println("- Antigüedad suficiente: " + antiguedadSuficiente);
        System.out.println("\n" + mensaje);
        
        scanner.close();
    }
}

Ejemplo 3: Operaciones con bits para permisos

public class SistemaPermisos {
    // Definimos constantes para permisos usando potencias de 2 (bits individuales)
    static final int PERMISO_LECTURA = 1;    // 001 en binario
    static final int PERMISO_ESCRITURA = 2;  // 010 en binario
    static final int PERMISO_EJECUCION = 4;  // 100 en binario
    
    public static void main(String[] args) {
        // Asignamos permisos: lectura y ejecución
        int permisosUsuario = PERMISO_LECTURA | PERMISO_EJECUCION;  // 001 | 100 = 101
        
        System.out.println("Permisos del usuario: " + permisosUsuario);
        
        // Verificamos si el usuario tiene permiso de lectura
        boolean tienePermisoLectura = (permisosUsuario & PERMISO_LECTURA) != 0;
        System.out.println("¿Tiene permiso de lectura? " + tienePermisoLectura);
        
        // Verificamos si el usuario tiene permiso de escritura
        boolean tienePermisoEscritura = (permisosUsuario & PERMISO_ESCRITURA) != 0;
        System.out.println("¿Tiene permiso de escritura? " + tienePermisoEscritura);
        
        // Añadimos permiso de escritura
        permisosUsuario |= PERMISO_ESCRITURA;  // 101 | 010 = 111
        System.out.println("Permisos después de añadir escritura: " + permisosUsuario);
        
        // Verificamos de nuevo el permiso de escritura
        tienePermisoEscritura = (permisosUsuario & PERMISO_ESCRITURA) != 0;
        System.out.println("¿Ahora tiene permiso de escritura? " + tienePermisoEscritura);
        
        // Revocamos el permiso de ejecución
        permisosUsuario &= ~PERMISO_EJECUCION;  // 111 & ~100 = 111 & 011 = 011
        System.out.println("Permisos después de revocar ejecución: " + permisosUsuario);
        
        // Verificamos si aún tiene permiso de ejecución
        boolean tienePermisoEjecucion = (permisosUsuario & PERMISO_EJECUCION) != 0;
        System.out.println("¿Todavía tiene permiso de ejecución? " + tienePermisoEjecucion);
    }
}

Resumen

En este artículo hemos explorado los operadores aritméticos, relacionales y lógicos en Java, herramientas fundamentales para manipular datos y tomar decisiones en nuestros programas. Hemos visto cómo realizar cálculos matemáticos con operadores aritméticos, comparar valores con operadores relacionales y combinar condiciones con operadores lógicos.

La comprensión adecuada de estos operadores y su precedencia es crucial para escribir código eficiente y libre de errores. Los operadores nos permiten construir expresiones complejas que son la base para las estructuras de control como condicionales y bucles, que veremos en próximos artículos. Dominar el uso de estos operadores te dará la capacidad de expresar lógica compleja de manera clara y concisa en tus programas Java.