danilo.pianini@unibo.it
gianluca.aguzzi@unibo.it
angelo.filaseta@unibo.it
Compiled on: 2025-10-23 — versione stampabile
static
static
java.lang.System.out
: potete usarlo senza avere un oggetto System
!
new System().out
!Integer.MAX_VALUE
Integer.parseInt(String s)
, Double.parseDouble(String s)
, etc.static
Classe.metodo(argomenti)
, e Classe.campo
main
dentro una classeIl metodo main
non richiede un oggetto per essere invocato
main
in quella classemain
come metodo statico di una classe (Java 25+)class EntryPoint {
static void main(String[] args) { // Main è un metodo statico della classe EntryPoint
System.out.println("Hello, World!");
}
}
In Java, anche se non obbligatorio, si adotta una convenzione di notazione per distinguere vari elementi del linguaggio
I nomi di classe iniziano in maiuscolo, e ogni parola “interna” al nome usa la maiuscola
PascalCase
, no underscore _
EnrolledStudent
, System
, Point3D
, Integer
, String
I nomi di campi istanza e metodi iniziano in minuscolo, e ogni parola “interna” al nome usa la maiuscola
camelCase
, no underscore _
firstName
, lastName
, getName()
, parseInt()
I nomi di campi static
sono interamente in maiuscolo, e ogni parola “interna” al nome è separata da un underscore _
SCREAMING_SNAKE_CASE
MAX_VALUE
, MIN_GUESS
, PI
static
)this
si riferisce all’istanza della classe (l’oggetto) che riceve il messaggiostatic
X
, possono essere visti come metodi e campi dell’unico oggetto “classe X
”java.lang.Math
contiene solo campi e metodi statici per operazioni matematicheclass Point3D { // dichiarazione classe
double x; // 3 campi
double y;
double z;
void build(double a, double b, double c) {
this.x = a;
this.y = b;
this.z = c;
}
double getModule() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
static Point3D ZERO = new Point3D(); // 0,0,0
static Point3D max(Point3D[] ps) { // metodo statico
Point3D max = ZERO; // ricerca max
for (Point3D elem : ps) {
if (elem.getModule() > max.getModule()) {
max = elem;
}
}
return max;
}
}
class UsePoint3D {
public static void main(String[] s) {
// creo e inizializzo vari punti
Point3D p1 = new Point3D();
p1.build(10, 20, 30);
Point3D p2 = new Point3D();
p2.build(5, 6, 7);
Point3D p3 = new Point3D();
p3.build(100, 100, 100);
Point3D p4 = Point3D.ZERO; // questo è lo zero
// costruisco l'array
Point3D[] array = new Point3D[] { p1, p2, p3, p4 };
// calcolo il max
Point3D max = Point3D.max(array);
// stampo
System.out.println("Max: " + max.x + "," + max.y + "," + max.z);
}
}
main
come metodo staticoNelle lezioni precedenti abbiamo usato la nuova sintassi (Java 25) che permette di scrivere direttamente un blocco main senza dichiarare esplicitamente la classe:
void main(String[] args) {
// codice
}
La sintassi canonica (classica) di Java richiede invece che main
sia un metodo statico contenuto in una classe:
class EntryPoint {
static void main(String[] args) {
// codice
}
}
Punti chiave:
main
è statico perché ha senso essere invocato senza creare un’istanza: la JVM lo chiama direttamente sulla classe.EntryPoint
è solo un nome di esempio: la classe che contiene main
può chiamarsi come si vuole, purché la JVM invii l’esecuzione a quel metodo.build
è sempre chiamata su un oggetto, ossia su: new Point3D()
zero
e max
sono chiamate sulla classe Point3D
max
è una funzionalità sui Point3D
, quindi è naturale includerla nella classe Point3D
, ma non è inquadrabile come funzionalità di un singolo oggetto (ognuno darebbe la stessa risposta)zero
è trattato come “costante”, quindi non è proprietà di un oggettoPoint3DBis
: inizializzazione con ritornoclass Point3DBis { // dichiarazione classe
double x; // 3 campi
double y;
double z;
Point3DBis build(double a, double b, double c) { // build con ritorno
this.x = a;
this.y = b;
this.z = c;
return this;
}
double getModule() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
static Point3DBis ZERO = new Point3DBis().build(0, 0, 0);
static Point3DBis max(Point3DBis[] ps) { // metodo statico
Point3DBis max = ZERO; // ricerca max
for (Point3DBis elem : ps) {
if (elem.getModule() > max.getModule()) {
max = elem;
}
}
return max;
}
}
Point3DBis
class UsePoint3DBis {
public static void main(String[] s) {
// creo vari punti
Point3DBis p1 = new Point3DBis().build(10, 20, 30);
Point3DBis p2 = new Point3DBis().build(5, 6, 7);
Point3DBis p3 = new Point3DBis().build(100, 100, 100);
Point3DBis p4 = Point3DBis.ZERO; // questo è lo zero
// costruisco l'array
Point3DBis[] array = new Point3DBis[] { p1, p2, p3, p4 };
// calcolo il max
Point3DBis max = Point3DBis.max(array);
// stampo
System.out.println("Max: " + max.x + "," + max.y + "," + max.z);
}
}
build
this
Point3D
// Classe Point3D con solo membri d'istanza (non-static)
class Point3D { // dichiarazione classe
double x; // 3 campi
double y;
double z;
Point3D build(double a, double b, double c) {
this.x = a;
this.y = b;
this.z = c;
return this;
}
double getModule() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
}
Points
class Points { // Modulo con funzionalità per punti
static Point3D zero = new Point3D().build(0, 0, 0);
static Point3D max(Point3D[] ps) { // metodo statico
Point3D max = zero; // ricerca max
for (Point3D elem : ps) {
if (elem.getModule() > max.getModule()) {
max = elem;
}
}
return max;
}
}
Point3D
e Points
class UsePoint3D {
static void main(String[] s) {
// creo e inizializzo vari punti
Point3D p1 = new Point3D().build(10, 20, 30);
Point3D p2 = new Point3D().build(5, 6, 7);
Point3D p3 = new Point3D().build(100, 100, 100);
Point3D p4 = Points.zero; // questo è lo zero
// costruisco l'array
Point3D[] array = new Point3D[] { p1, p2, p3, p4 };
// calcolo il max
Point3D max = Points.max(array);
// stampo
System.out.println("Max: " + max.x + "," + max.y + "," + max.z);
}
}
new
new T
crea un oggetto di tipo T
inizializzando tutti i suoi campi al loro valore di default (p.e. $0$ per i numeri), e ne restituisce il riferimentoPoint3D p = new Point3D();
Point3D[] ps = new Point3D[2]; // [null, null] <- sintassi per creare array di oggetti
new
si possono quindi passare dei valorinew Point3D()
<NomeClasse>(<Tipo1> <arg1>, <Tipo2> <arg2>, ...) {
// codice di inizializzazione
}
public class Point3Dcons { // dichiarazione classe
double x;
double y;
double z;
// costruttore
Point3Dcons(double inx,double iny,double inz){ // nota: assenza di tipo di ritorno
this.x = inx; // metto l'argomento inx in this.x
this.y = iny; // ..
this.z = inz; // ..
}
public static void main(String[] args){
// creo l'oggetto usando il costruttore a tre argomenti
Point3Dcons p = new Point3Dcons(10.0, 20.0, 30.0);
// stampo
System.out.println("p: " + p.x + "," + p.y + "," + p.z);
// costruttore di "default" in questo caso non funziona!
// Point3D p2 = new Point3D(); NO!!
}
}
public class Point3Dcons { // dichiarazione classe
double x;
double y;
double z;
// costruttore
Point3Dcons(double inx,double iny,double inz){ // nota: assenza di tipo di ritorno
this.x = inx; // metto l'argomento inx in this.x
this.y = iny; // ..
this.z = inz; // ..
}
public static void main(String[] args){
// creo l'oggetto usando il costruttore a tre argomenti
Point3Dcons p = new Point3Dcons(10.0, 20.0, 30.0);
// stampo
System.out.println("p: " + p.x + "," + p.y + "," + p.z);
// costruttore di "default" in questo caso non funziona!
// Point3D p2 = new Point3D(); NO!!
}
}
È una prassi comune, ma non una regola (tutt’altro)
import java.util.Calendar;
public class Person { // dichiarazione classe
String name;
int birthYear;
boolean isMarried;
Person(String nome) {
this.name = nome;
this.birthYear = Person.currentYear;
this.isMarried = false;
}
Person(String nome, int annoNascita) {
this.name = nome;
this.birthYear = annoNascita;
this.isMarried = false;
}
Person(String nome, int annoNascita, boolean sposato) {
this.name = nome;
this.birthYear = annoNascita;
this.isMarried = sposato;
}
static int currentYear = Calendar.getInstance().get(Calendar.YEAR);
}
public class UsePerson {
public static void main(String[] s) {
// Person p1 = new Person(); NO!!
Person p2 = new Person("Mario Rossi");
Person p3 = new Person("Gino Bianchi", 1979);
Person p4 = new Person("Carlo Verdi", 1971, true);
Person[] persone = new Person[]{ p2, p3, p4 };
for(int i=0; i < persone.length; i++){
System.out.println(persone[i].name + ", nato/a nel " + persone[i].birthYear +
(persone[i].isMarried ? "" : ", non") + " è sposato/a.");
}
}
}
new
this
punta all’oggetto creato)new
restituisce il riferimento this
this(..)
import java.util.Calendar;
class Person2 { // dichiarazione classe
String name;
int birthYear;
boolean isMarried;
Person2(String nome, int yearOfBirth, boolean isMarried) { // costruttore completo
this.name = nome;
this.birthYear = yearOfBirth;
this.isMarried = isMarried;
}
Person2(String name, int birthYear) { // richiama costruttore a 3 arg..
this(name, birthYear, false);
}
Person2(String name) {
this(name, Person2.CURRENT_YEAR); // richiama costruttore a 2 arg..
}
static int CURRENT_YEAR = Calendar.getInstance().get(Calendar.YEAR);
}
+
) e overloading costruttoriData una new
, quale costruttore richiamerà? (JLS 15.12)
class ExampleOverloading {
static int m(double a, int b) { return 1; }
static int m(int a, double b) { return 2; }
static int m2(double a, double b) { return 1; }
static int m2(int a, int b) { return 2; }
public static void main(String[] s) {
// System.out.println(""+m(1, 1)); // ERROR: reference to m is AMBIGUOUS
// System.out.println(""+m(1.5, 1.5)); // ERROR: no suitable method found for m(Double,Double)
System.out.println("" + m(1.5, 1));
System.out.println("" + m((float)1, 1));
System.out.println("" + m(1, 1.5));
System.out.println("" + m2(1.5, 1.5)); // 1
System.out.println("" + m2(1, 1)); // 2
}
}
n1.n2....nj
(p.e. java.lang
).java
siano organizzati in directory che rispecchiano la struttura del package
n1/n2/Foo.java
per la classe Foo
del package n1.n2
.class
:
su Linux, ;
su Windows) in cui cercare i package-cp
di javac
e java
o con la variabile d’ambiente CLASSPATH
.
).java
) deve specificare il suo packagepackage pname;
new
o come tipo) specificando anche il package (p.e. new java.util.Date()
)import java.util.*;
importa tutte le classi di java.util
import java.util.Date;
importa la sola java.util.Date
javac
.java
public
e private
public
– visibile da tutte le classipublic
private
– visibile solo nella classe correnteprivate
public
.java
public
/private
può consentire di gestire a piacimento il concetto di information hiding
main
classico di Java (Java 24 e precedenti)Il metodo main
ha le seguenti caratteristiche:
static
public
main
come metodo statico e pubblico di una classeclass EntryPoint {
public static void main(String[] args) { // Main è un metodo statico della classe EntryPoint
System.out.println("Hello, World!");
}
}
Se usate una versione di Java precedente alla 25, dovete sempre usare questa sintassi.
final
final
= non modificabilepublic static final int CONST=10;
public
, si ha la garanzia che nessuno le modifichipublic class MagicExample {
// Put 100 into a constant and give it a name!!
private static final int SIZE = 100;
public static void main(String[] s) {
double[] array = new double[SIZE];
double sum = 0;
for (int i = 0; i < SIZE; i++) {
// Assegno un numero random
array[i] = Math.random();
sum = sum + array[i];
}
System.out.println("Somma " + sum);
}
}
GuessMyNumberApp
: un esempio completoimport java.util.Random;
public class GuessMyNumberApp {
public static final int ATTEMPTS = 10;
public static final int MAX_GUESS = 100;
public static final int MIN_GUESS = 1;
public static void main(String[] args) {
int number = new Random().nextInt(MAX_GUESS - MIN_GUESS) + MIN_GUESS;
for (int i = 1; i <= ATTEMPTS; i++){
System.out.println("Attempt no. "+i);
System.out.println("Insert your guess.. ");
int guess = Integer.parseInt(System.console().readLine());
if (guess == number){
System.out.println("You won!!");
return;
} else if (guess > number){
System.out.println("Your guess is greater..");
} else {
System.out.println("Your guess is lower..");
}
}
System.out.println("Sorry, you lost!");
}
}
new Random().nextInt(MAX_GUESS - MIN_GUESS) + MIN_GUESS
nextInt(n)
genera numero tra 0 e n-1MIN_GUESS
per ottenere l’intervallo desideratoSystem.console().readLine()
legge una riga da consoleInteger.parseInt()
converte la stringa in interonew
malloc
del Cnew
chiama il gestore della memoria, che alloca lo spazio necessariofree
del C) senza che il programmatore debba occuparsenepublic class GC {
private static long size = 1000;
public static void main(String[] s) {
// Runtime dà info sull'esecuzione
Runtime r = Runtime.getRuntime();
// Creo oggetti all'infinito
for (long l = 0; true; l++) {
new Object();
// Stampo solo ogni tanto
if (l % size == 0) {
System.out.print("Objs (*10^6): " + l / 1000000);
System.out.println(" Freemem (MB):" + (r.freeMemory() >> 20));
}
// La memoria libera si vedrà calare lentamente
// e poi riprendersi di colpo, ciclicamente
}
}
}
GuessMyNumber
GuessMyNumberGame
: la classe principale che gestisce il flusso del gioco
play()
: contiene il ciclo di giocoNumberPicker
: una classe che contiene le logiche per generare numeri casuali
pickNumber(int min, int max)
: genera un numero casuale nell’intervallo specificatoIOManager
: una classe che gestisce l’input e l’output
readIntInRange(int min, int max)
: legge il tentativo del giocatoreprintLine(String message)
: stampa un messaggio sulla consoleGameConfig
Questa classe non era necessaria, ma aiuta a raggruppare le costanti di configurazione del gioco.
public class GameConfig {
private final int attempts;
private final int minGuesses;
private final int maxGuesses;
public GameConfig(int attempts, int minGuesses, int maxGuesses) {
this.attempts = attempts;
this.minGuesses = minGuesses;
this.maxGuesses = maxGuesses;
}
public int getAttempts() {
return this.attempts;
}
public int getMinGuesses() {
return this.minGuesses;
}
public int getMaxGuesses() {
return this.maxGuesses;
}
}
NumberPicker
public class NumberPicker {
private final Random random;
public NumberPicker() {
random = new Random();
}
public NumberPicker(int seed) {
random = new Random(seed);
}
public int pickNumber(int minInclusive, int maxExclusive) {
return random.nextInt((maxExclusive - minInclusive) + 1) + minInclusive;
}
}
IOManager
import java.util.Scanner;
public class IOManager {
private final Scanner scanner;
public IOManager() {
this.scanner = new Scanner(System.in);
}
public void printLine(String text) {
System.out.println(text);
}
public int readIntInRange(int min, int max) {
while (true) {
String line = scanner.nextLine();
int value = Integer.parseInt(line.trim());
if (value < min || value > max) {
System.out.println("Please enter a number between " + min + " and " + max + ".");
} else {
return value;
}
}
}
}
GuessMyNumberGame
public class GuessMyNumberGame {
private final GameConfig config;
private final NumberPicker numberPicker;
private final IOManager io;
public GuessMyNumberGame(GameConfig config, NumberPicker numberPicker, IOManager io) {
this.config = config;
this.numberPicker = numberPicker;
this.io = io;
}
public void play() {
int secret = numberPicker.pickNumber(config.getMinGuesses(), config.getMaxGuesses());
for (int attempt = 1; attempt <= config.getAttempts(); attempt++) {
io.printLine("Attempt no. " + attempt);
io.printLine("Insert your guess (" + config.getMinGuesses() + "–" + config.getMaxGuesses() + "):");
int guess = io.readIntInRange(config.getMinGuesses(), config.getMaxGuesses());
if (guess == secret) {
io.printLine("You won!!");
return;
}
if (guess > secret) {
io.printLine("Your guess is greater..");
} else {
io.printLine("Your guess is lower..");
}
}
io.printLine("Sorry, you lost!");
}
}
main
import it.unibo.lifecycle.modeling.IOManager;
import it.unibo.lifecycle.modeling.GameConfig;
import it.unibo.lifecycle.modeling.NumberPicker;
void main() {
GameConfig config = new GameConfig(10, 1, 100);
NumberPicker numberPicker = new NumberPicker();
IOManager consoleManager = new IOManager();
GuessMyNumberGame game = new GuessMyNumberGame(config, numberPicker, consoleManager);
game.play();
}
IOManager
va modificata.main
: il main
si limita a inizializzare e avviare il gioco, lasciando la logica nelle classi dedicate per un flusso principale chiaro e intuitivo.danilo.pianini@unibo.it
gianluca.aguzzi@unibo.it
angelo.filaseta@unibo.it
Compiled on: 2025-10-23 — versione stampabile