Operatori Java: guida completa e tecnica per sviluppatori
In questa guida approfondita illustriamo tutti i principali operatori del linguaggio Java (aritmetici, relazionali, logici, bitwise, di assegnazione, ternario, instanceof). In particolare, troverai spiegazioni tecniche, note sulle insidie comuni ed esempi di codice completi e funzionanti.

1. Panoramica sugli operatori in Java
In Java, un operatore è un simbolo che indica al compilatore di eseguire una specifica operazione su uno o più operandi. Inoltre, gli operatori sono definiti a livello di linguaggio e rispettano regole di precedenza e associatività — documentate ufficialmente da Oracle — che, di conseguenza, influenzano l’ordine di valutazione delle espressioni.
Le principali categorie di operatori che tratteremo sono, in particolare:
- Operatori aritmetici
- Operatori relazionali
- Operatori logici (condizionali)
- Operatori bitwise (a livello di bit)
- Operatori di assegnazione (semplici e composti)
- Operatore ternario
? : - Operatore
instanceof
Tutti gli esempi sono in Java 8+ e, pertanto, possono essere compilati ed eseguiti con javac e java.
2. Operatori aritmetici
Gli operatori aritmetici operano su tipi numerici (byte, short, int, long, float, double, e in parte char). In particolare, supportano sia operazioni binarie (due operandi) sia unarie (un operando).
+(somma / unario positivo)-(sottrazione / unario negativo)*(moltiplicazione)/(divisione)%(resto, modulo)++(incremento, prefisso o suffisso)--(decremento, prefisso o suffisso)
2.1 Esempio base con operatori aritmetici
Esempio completo che mostra somma, differenza, prodotto, divisione e modulo, con attenzione al comportamento della divisione intera:
public class OperatoriAritmeticiBase {
public static void main(String[] args) {
int a = 7;
int b = 3;
int somma = a + b; // 10
int differenza = a - b; // 4
int prodotto = a * b; // 21
int divisioneIntera = a / b; // 2 (troncamento)
int resto = a % b; // 1
double divisioneReale = a / (double) b; // 2.333...
System.out.println("somma = " + somma);
System.out.println("differenza = " + differenza);
System.out.println("prodotto = " + prodotto);
System.out.println("divisioneIntera = " + divisioneIntera);
System.out.println("resto = " + resto);
System.out.println("divisioneReale = " + divisioneReale);
}
}
Nota: la divisione tra interi in Java tronca la parte decimale. A tal fine, per ottenere un risultato con virgola è necessario effettuare un cast a double o float.
2.2 Operatori Java di incremento e decremento: prefisso vs suffisso
Gli operatori ++ e -- hanno, inoltre, un comportamento diverso a seconda che vengano usati in prefisso o suffisso:
++x: incrementax, restituisce il valore incrementatox++: restituisce il valore corrente dix, poi lo incrementa

public class PrefissoSuffissoDemo {
public static void main(String[] args) {
int x = 5;
int a = ++x; // x diventa 6, a = 6
int b = x++; // b = 6, poi x diventa 7
System.out.println("x = " + x); // 7
System.out.println("a = " + a); // 6
System.out.println("b = " + b); // 6
}
}
2.3 Insidie degli operatori Java: overflow e promozione numerica
Gli operatori aritmetici su tipi interi non lanciano eccezioni in caso di overflow: di conseguenza, il risultato è “wrap-around” secondo il complemento a due. Inoltre, le operazioni tra tipi più piccoli di int vengono promosse ad int.
public class OverflowPromozioneDemo {
public static void main(String[] args) {
int max = Integer.MAX_VALUE; // 2147483647
int overflow = max + 1; // -2147483648
System.out.println("max = " + max);
System.out.println("overflow = " + overflow);
byte b1 = 100;
byte b2 = 27;
// byte somma = b1 + b2; // ERRORE di compilazione: il risultato è int
byte sommaCorretta = (byte) (b1 + b2);
System.out.println("sommaCorretta = " + sommaCorretta); // 127
}
}
3. Operatori relazionali
Gli operatori relazionali confrontano due valori e, dunque, restituiscono un risultato booleano (true o false).
==(uguale)!=(diverso)>(maggiore)>=(maggiore o uguale)<(minore)<=(minore o uguale)
3.1 Operatori Java: confronto tra tipi primitivi
public class OperatoriRelazionaliPrimitivi {
public static void main(String[] args) {
int x = 10;
int y = 20;
System.out.println("x == y? " + (x == y)); // false
System.out.println("x != y? " + (x != y)); // true
System.out.println("x < y? " + (x < y)); // true
System.out.println("x <= 10? " + (x <= 10)); // true
System.out.println("y > 15? " + (y > 15)); // true
System.out.println("y >= 20? " + (y >= 20)); // true
}
}
3.2 Operatori in Java: confronto tra oggetti e l’insidia di ==
Per i riferimenti ad oggetto, == confronta l’identità (stesso riferimento), non l’uguaglianza logica. Invece, per confrontare il contenuto, bisogna usare equals.
public class ConfrontoOggettiDemo {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println("s1 == s2? " + (s1 == s2)); // false, riferimenti diversi
System.out.println("s1.equals(s2)? " + s1.equals(s2)); // true, stesso contenuto
String s3 = "world";
String s4 = "world";
System.out.println("s3 == s4? " + (s3 == s4)); // true (pool di stringhe), ma non farci affidamento per logica di business
}
}
4. Operatori logici (condizionali)
Gli operatori logici operano su valori booleani. In particolare, in Java abbiamo sia versioni “short-circuit” sia versioni “bitwise” applicate a boolean (da non confondere con gli operatori bitwise su interi):
&&(AND logico, short-circuit)||(OR logico, short-circuit)!(NOT logico, unario)&(AND non short-circuit, spesso usato come bitwise)|(OR non short-circuit, spesso usato come bitwise)^(XOR logico / bitwise)
4.1 Operatori logici Java: short-circuit vs non short-circuit
Gli operatori && e || valutano il secondo operando solo se necessario (short-circuit). Invece, gli operatori & e | valutano sempre entrambi gli operandi, anche se il risultato è già determinato dal primo.
public class ShortCircuitDemo {
public static void main(String[] args) {
boolean result1 = metodoTrue() && metodoFalse();
System.out.println("result1 = " + result1);
boolean result2 = metodoTrue() & metodoFalse();
System.out.println("result2 = " + result2);
}
private static boolean metodoTrue() {
System.out.println("metodoTrue()");
return true;
}
private static boolean metodoFalse() {
System.out.println("metodoFalse()");
return false;
}
}
Output atteso:
metodoTrue()
result1 = false
metodoTrue()
metodoFalse()
result2 = false
Nel primo caso metodoFalse() non viene chiamato perché true && ? richiede la valutazione del secondo operando, ma se fosse false && ? non verrebbe eseguito. Pertanto, nell’esempio invertendo metodoTrue() e metodoFalse() la differenza sarebbe ancora più evidente.
4.2 Operatori Java: combinazione avanzata di espressioni logiche
public class ValidazioneInputDemo {
public static void main(String[] args) {
String username = "admin";
String password = "123456";
boolean usernameValido = username != null && !username.isEmpty();
boolean passwordValida = password != null && password.length() >= 6;
boolean autenticato = usernameValido && passwordValida &&
("admin".equals(username) && "123456".equals(password));
System.out.println("usernameValido = " + usernameValido);
System.out.println("passwordValida = " + passwordValida);
System.out.println("autenticato = " + autenticato);
}
}
5. Operatori bitwise (a livello di bit)
Gli operatori bitwise lavorano sulla rappresentazione binaria dei tipi interi (byte, short, int, long). Di conseguenza, sono fondamentali per operazioni a basso livello, flag, maschere e ottimizzazioni.
&(AND bitwise)|(OR bitwise)^(XOR bitwise)~(NOT bitwise, unario)<<(shift a sinistra con segno)>>(shift a destra con segno)>>>(shift a destra senza segno)
5.1 Operatori bitwise Java: flag e maschere di bit
Supponiamo di codificare permessi con flag. Ad esempio, ogni bit rappresenta un permesso:
public class BitwiseFlagDemo {
private static final int READ = 1 << 0; // 0001
private static final int WRITE = 1 << 1; // 0010
private static final int EXECUTE = 1 << 2; // 0100
private static final int DELETE = 1 << 3; // 1000
public static void main(String[] args) {
int userPermissions = READ | WRITE; // 0011
System.out.println("userPermissions (bin) = " + toBinary(userPermissions));
boolean canRead = (userPermissions & READ) != 0;
boolean canWrite = (userPermissions & WRITE) != 0;
boolean canExecute = (userPermissions & EXECUTE) != 0;
System.out.println("canRead = " + canRead);
System.out.println("canWrite = " + canWrite);
System.out.println("canExecute = " + canExecute);
// Aggiungiamo EXECUTE
userPermissions |= EXECUTE; // OR composto
System.out.println("after EXECUTE (bin) = " + toBinary(userPermissions));
// Rimuoviamo WRITE
userPermissions &= ~WRITE; // AND con negazione della maschera
System.out.println("after remove WRITE (bin) = " + toBinary(userPermissions));
}
private static String toBinary(int value) {
return String.format("%4s", Integer.toBinaryString(value)).replace(' ', '0');
}
}
5.2 Operatori Java di shift a sinistra e a destra
Gli shift spostano i bit a sinistra o destra. Nello specifico, >> preserva il bit di segno (shift aritmetico), mentre >> inserisce zeri (shift logico).
public class ShiftDemo {
public static void main(String[] args) {
int x = 8; // 0000 1000
System.out.println("x = " + x + " (" + Integer.toBinaryString(x) + ")");
int left = x << 1; // 0001 0000 = 16
int right = x >> 1; // 0000 0100 = 4
System.out.println("left = " + left + " (" + Integer.toBinaryString(left) + ")");
System.out.println("right = " + right + " (" + Integer.toBinaryString(right) + ")");
int negative = -8;
System.out.println("negative = " + negative + " (" + Integer.toBinaryString(negative) + ")");
int rightSigned = negative >> 1;
int rightUnsigned = negative >>> 1;
System.out.println("rightSigned = " + rightSigned + " (" + Integer.toBinaryString(rightSigned) + ")");
System.out.println("rightUnsigned = " + rightUnsigned + " (" + Integer.toBinaryString(rightUnsigned) + ")");
}
}
6. Operatori di assegnazione
Oltre all’operatore di assegnazione semplice =, Java supporta, inoltre, una serie di operatori di assegnazione composti che combinano un’operazione aritmetica o bitwise con l’assegnazione.
=+=,-=,*=,/=,%=&=,|=,^=<<=,>>=,>>>=
public class OperatoriAssegnazioneDemo {
public static void main(String[] args) {
int x = 10;
x += 5; // x = x + 5 => 15
x *= 2; // x = x * 2 => 30
x -= 4; // x = x - 4 => 26
x /= 2; // x = x / 2 => 13
x %= 5; // x = x % 5 => 3
System.out.println("x finale = " + x);
int flags = 0;
flags |= 0b0001; // set bit 0
flags |= 0b0010; // set bit 1
flags &= ~0b0001; // clear bit 0
System.out.println("flags = " + Integer.toBinaryString(flags)); // 10
int y = 1;
y <<= 3; // y = y << 3 => 8
System.out.println("y = " + y);
}
}
7. Operatore ternario ? :
L’operatore ternario è, in altre parole, una forma compatta di if-else. La sintassi è:
condizione ? espressioneSeVero : espressioneSeFalso
La condizione deve essere un booleano; di conseguenza, il risultato è il valore dell’espressione selezionata.
7.1 Operatore ternario Java: esempio base
public class OperatoreTernarioBase {
public static void main(String[] args) {
int eta = 18;
String categoria = (eta >= 18) ? "adulto" : "minorenne";
System.out.println("Categoria: " + categoria);
}
}
7.2 Operatore ternario Java: annidamento e tipi
Quando si annidano operatori ternari, la leggibilità diminuisce rapidamente. Per questo motivo, in questi casi è spesso preferibile usare if-else espliciti. Inoltre, Java applica regole di promozione dei tipi: le due espressioni devono essere compatibili.
public class OperatoreTernarioAvanzato {
public static void main(String[] args) {
int score = 87;
String grade = score >= 90 ? "A" :
score >= 80 ? "B" :
score >= 70 ? "C" :
score >= 60 ? "D" : "F";
System.out.println("Voto: " + grade);
// Attenzione alla compatibilità dei tipi
int x = 5;
// double y = true ? x : 2.5; // OK, x viene promosso a double
double y = true ? x : 2.5;
System.out.println("y = " + y);
}
}
8. Operatore instanceof
L’operatore instanceof verifica se un oggetto è un’istanza di una certa classe, sottoclasse o, anche, implementazione di interfaccia. Pertanto, restituisce un booleano.
espressioneOggetto instanceof Tipo
8.1 Operatore instanceof Java: esempio con gerarchie di classi
class Animale {}
class Cane extends Animale {}
class Gatto extends Animale {}
public class InstanceofDemo {
public static void main(String[] args) {
Animale a = new Cane();
System.out.println("a instanceof Cane? " + (a instanceof Cane)); // true
System.out.println("a instanceof Gatto? " + (a instanceof Gatto)); // false
System.out.println("a instanceof Animale? " + (a instanceof Animale)); // true
System.out.println("a instanceof Object? " + (a instanceof Object)); // true
a = null;
System.out.println("null instanceof Animale? " + (a instanceof Animale)); // false
}
}
8.2 Pattern matching per instanceof (Java 16+)
Dalle versioni moderne di Java (16+), è supportato il pattern matching per instanceof, che permette di combinare il controllo di tipo con il cast in un’unica espressione. Quindi, questa risulta essere più leggibile.
public class InstanceofPatternMatchingDemo {
public static void main(String[] args) {
Object obj = "Hello";
if (obj instanceof String s && s.length() > 3) {
System.out.println("String con lunghezza > 3: " + s.toUpperCase());
}
Object maybeInteger = 42;
if (maybeInteger instanceof Integer i) {
System.out.println("Valore raddoppiato: " + (i * 2));
}
}
}
9. Combinare diversi operatori: esempio completo
L’esempio seguente combina operatori aritmetici, relazionali, logici, bitwise, di assegnazione, ternario e instanceof in un piccolo motore di regole. In particolare, questo motore calcola il prezzo finale di un ordine.
import java.util.Objects;
enum TipoCliente {
STANDARD, PREMIUM, VIP
}
class Ordine {
final TipoCliente tipoCliente;
final double importoBase;
final boolean applicaIVA;
final boolean urgente;
Ordine(TipoCliente tipoCliente, double importoBase, boolean applicaIVA, boolean urgente) {
this.tipoCliente = Objects.requireNonNull(tipoCliente);
this.importoBase = importoBase;
this.applicaIVA = applicaIVA;
this.urgente = urgente;
}
}
public class MotorePrezziDemo {
private static final double IVA = 0.22;
public static void main(String[] args) {
Ordine o1 = new Ordine(TipoCliente.STANDARD, 100, true, false);
Ordine o2 = new Ordine(TipoCliente.PREMIUM, 250, true, true);
Ordine o3 = new Ordine(TipoCliente.VIP, 1000, false, true);
calcolaEStampa(o1);
calcolaEStampa(o2);
calcolaEStampa(o3);
}
private static void calcolaEStampa(Object ordine) {
if (!(ordine instanceof Ordine o)) {
throw new IllegalArgumentException("Oggetto non valido: " + ordine);
}
double prezzo = o.importoBase;
// Sconto base in base al tipo cliente (operatore ternario)
double sconto = switch (o.tipoCliente) {
case STANDARD -> 0.0;
case PREMIUM -> 0.10;
case VIP -> 0.20;
};
prezzo -= prezzo * sconto; // operatore di assegnazione composto
// Supplemento urgenza
if (o.urgente && prezzo > 100) { // operatori logici e relazionali
prezzo += 15; // fisso
}
// Applicazione IVA
if (o.applicaIVA) {
prezzo *= (1 + IVA); // operatore aritmetico + assegnazione composta
}
// Calcolo "codice rischio" bitwise (esempio puramente dimostrativo)
int riskCode = 0;
riskCode |= o.importoBase > 500 ? 0b0001 : 0;
riskCode |= o.urgente ? 0b0010 : 0;
riskCode |= o.tipoCliente == TipoCliente.STANDARD ? 0b0100 : 0;
boolean highRisk = (riskCode & 0b0011) != 0; // operatore bitwise
System.out.printf("Cliente: %s, importoBase: %.2f, prezzoFinale: %.2f, highRisk=%b%n",
o.tipoCliente, o.importoBase, prezzo, highRisk);
}
}
10. Conclusioni e buone pratiche
- Usa gli operatori aritmetici con attenzione all’overflow e ai tipi (
intvslong,doublevsfloat). - Per confrontare oggetti usa sempre
equals(tranne casi specifici in cui serve verificare l’identità con==). - Preferisci
&&e||a&e|nelle espressioni booleane per sfruttare lo short-circuit. - Limita l’uso di operatori ternari annidati: se la logica diventa complessa, passa a
if-elseo a unswitch. - Per
instanceof, sfrutta il pattern matching nelle versioni moderne di Java per ridurre il boilerplate. - Quando usi gli operatori bitwise, incapsula la logica in metodi ben nominati per preservare la leggibilità.
Comprendere a fondo gli operatori Java è fondamentale per scrivere codice corretto, leggibile ed efficiente. In sintesi, questo è ancora più vero in sistemi complessi dove operazioni aritmetiche, logiche e a basso livello sono combinate tra loro.
Lascia un commento