Ingegneria del Software - Java

Installazione e primi passi in Java.

Obiettivi

  • Installare Java
  • Configurare l'ambiente di sviluppo (VsCode)
  • Concetti base e keyword
  • Il primo hello world
  • Parametri da linea di comando
  • Input utente
  • Leggere e scrivere su file

Java

Java è un linguaggio di programmazione sviluppato da Sun Microsystems nel 1995.
Si tratta un linguaggio ad alto livello orientato agli oggetti e fortemente tipizzato.

Compile once, run everywhere

Dopo averlo compilato i programmi in bytecode, questo viene eseguito su una macchina virtuale (JVM), disponibile per tutti i sistemi operativi.
Il codice scritto in Java può essere eseguito su qualsiasi sistema, al costo di una penalità di prestazioni.

Installazione

Windows

  • Scaricare l'installer (msi) da qui
  • Lanciare l'installer

MAC

Homebrew

Manualmente

  • Scaricare l'installer (tar.gz) per la versione 17.0.2 da qui
  • Decomprimere il file tar -xf <nome_fiel>.tar.gz -C <destinazione>
  • Esportate le variabili d'ambiente export JAVA_HOME=<destinazione>/jdk-17.0.2.jdk/Contents/Home e export PATH=\$JAVA_HOME/bin:\$PATH

Linux

Utilizzate il gestore dei pacchetti del vostro sistema operativo.

Ubuntu

sudo apt install openjdk-17-jdk

Arch

sudo pacman -S jdk17-openjdk

Fedora

sudo dnf install java-17-openjdk

Cambiare versione di Java

Se si hanno installate più versioni di Java, è possibile cambiare la versione predefinita.

Windows

Modificare la variabile d'ambiente JAVA_HOME con il percorso della nuova versione di Java.

System Properties > Environment Variables > System variables > JAVA_HOME

Ulteriori dettagli

MAC

Bisogna modificare la variabile d'ambiente JAVA_HOME con il percorso della nuova versione di Java.

/usr/libexec/java_home -V # mostra le versioni di Java installate
export JAVA_HOME=`/usr/libexec/java_home -v <versione>` # cambia la versione di Java a quella indicata

Per cambiare la versione di Java in maniera permanente, bisogna aggiungere una riga simile al file ~/.bash_profile, ~/.bashrc o ~/.zshrc.

# ~/.bash_profile
export JAVA_HOME=`/usr/libexec/java_home -v 17`

# ~/.bashrc
export JAVA_HOME=`/usr/libexec/java_home -v 17`

# ~/.zshrc
export JAVA_HOME=$(/usr/libexec/java_home -v 17)

# ~/.fishrc
set -x JAVA_HOME (/usr/libexec/java_home -d64 -v 17)

Ulteriori dettagli

Linux

Vale la stessa logica di MAC per l'update della variabile d'ambiente JAVA_HOME.

Ubuntu

sudo update-alternatives --config java # mostra le versioni di Java installate e permette di sceglierne una
sudo update-alternatives --config javac # mostra le versioni di javac installate e permette di sceglierne una

Arch

sudo archlinux-java status # mostra le versioni di Java installate
sudo archlinux-java set java-17-openjdk # cambia la versione di Java a quella indicata

Java Locale

Quando viene installata, la JVM seleziona un locale predefinito.
Per visualizzare il locale corrente, utilizzare il comando:

java -XshowSettings:locale -version

Per cambiare il locale da riga di comando:

java -Duser.language=<language> -Duser.region=<region> <nome_file>.java

o direttamente nel codice:

Locale.setDefault(Locale.ITALY);

Ulteriori dettagli

Comandi utili per Java

  • java -version
    • mostra la versione di Java installata
  • java -jar <nome_file>.jar
    • esegue un file jar
  • javac <nome_file>.java
    • compila un file java in bytecode (.class)
  • java <nome_file>
    • esegue un file java
  • javap <nome_file>.class
    • decompila un file file bytecode restituendo il codice sorgente, se possibile
  • jar cf <nome_output_file>.jar <files>.class
    • crea un file jar archivio contenente i file class
  • jar cfe <nome_output_file>.jar <main_class> <files>.class
    • crea un file jar eseguibile contenente i file class

Editor

Intellij

Eclipse

VsCode

Concetti base

Alcuni concetti fondamentali di Java e della programmazione orientata agli oggetti.

Linguaggio orientato agli oggetti

Java è un linguaggio fortemente orientato agli oggetti.

Ogni programma può essere visto come una collezione di oggetti che interagiscono tra loro.

I design pattern ci aiutano a strutturare il codice al fine di renderlo più mantenibile e leggibile anche per progetti particolarmente complessi.

L'esecuzione di un programma Java parte da un metodo main con una firma ben precisa che si trova nella classe indicata.

Classe vs Oggetto

Una classe è un modello che descrive un oggetto. Un oggetto è una istanza di una classe.

Piantina vs Casa

Keyword

Le parole chiave riservate a Java.

Static

class StaticClass {
    private static String staticVar = "I'm static";
    public static void staticMethod() {
        System.out.println("Static variable value: " + staticVar);
    }
}

Indica metodi o variabili appartenenti alla classe e non all'oggetto.

Possono essere chiamati senza aver creato un oggetto.

Hanno accesso solo ad altri metodi o variabili statici.

Final

final class FinalClass {
    private final String FINAL_VAR = "I'm final";
    public final void finalMethod() {
        // FINAL_VAR = "I'm not final anymore"; // Error
        System.out.println("Final variable value: " + FINAL_VAR);
    }
}

Si tratta di variabili, metodi o classi che non possono essere modificati.

Variabili: Non possono essere riassegnate.

Metodi: Non possono essere sovrascritti da classi derivate.

Classi: Non possono essere estese da altre classi.

Interface

interface Interface {
    public void interfaceMethod();
}

Si tratta di un insieme di metodi astratti.

Non possono contenere variabili.

Abstract

abstract class AbstractClass {
    public abstract void abstractMethod();
    public void definedMethod() {
        System.out.println("I'm defined");
    }
}

Si tratta di classi che non possono essere istanziate.

Possono contenere metodi astratti: senza un corpo, ma con una firma.

Access modifiers

class MyClass {
    private int privateVar;
    protected int protectedVar;
    int defaultVar;
    public int publicVar;
}

Servono ad indicare il livello di visibilità di una variabile, metodo o classe.

private: Accessibile solo all'interno della classe.

protected: Accessibile all'interno della classe e delle classi derivate.

default: Accessibile da tutte le classi dello stesso package.

public: Accessibile ovunque.

Override

class BaseClass {
    public void method() {
        System.out.println("BaseClass method");
    }
}

class DerivedClass extends BaseClass {
    @Override
    public void method() {
        System.out.println("DerivedClass method");
    }
}

Metodo sovrascritto da una classe derivata.

Il metodo deve avere la stessa firma del metodo sovrascritto.

È possibile richiamare il metodo sovrascritto con la parola chiave super.<metodo>(args).

Overload

class OverloadClass {
    public void method() {
        System.out.println("No parameters");
    }
    public void method(int a) {
        System.out.println("One parameter");
    }
    public void method(int a, int b) {
        System.out.println("Two parameters");
    }
}

Indica che un metodo è stato definito più volte con lo stesso nome ma con parametri diversi.

Hello World

Il primo programma di ogni buon informatico.

Codice

// HelloWorld.java

class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

Compilazione ed esecuzione

javac HelloWorld.java
java HelloWorld

Parametri da linea di comando

Quando si lancia un programma da linea di comando, è possibile passare dei parametri che saranno poi accessibili al programma stesso sotto forma di un array di stringhe.

public static void main(String[] args) {
    // ...
}

Invocare il programma

Quando viene lanciato il programma, gli argomenti che seguono il nome del programma vengono passati al metodo main nel parametro args.

java Calculator 1 + 2
  • java: invoca la JVM
  • Calculator: nome della classe che contiene il metodo main
  • 1: primo parametro (args[0])
  • +: secondo parametro (args[1])
  • 2: terzo parametro (args[2])

Codice

// Calculator.java

class Calculator {
    private static int calculateResult(int a, int b, String op) {
        switch (op) {
            case "+":
                return a + b;
            case "-":
                return a - b;
            case "x":
                return a * b;
            case "/":
                return a / b;
            default:
                System.err.println("Unknown operator");
                System.exit(1);
                return 0;
        }
    }

    public static void main(String[] args) {
        if (args.length != 3) {
            System.err.println("Usage: java Calculator <a> <op> <b>");
            System.err.println("<op> can be +, -, x, /");
            System.exit(1);
        }

        int a = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[2]);
        int result = calculateResult(a, b, args[1]);

        System.out.println(result);
    }
}

Compilazione ed esecuzione

javac Calculator.java
java Calculator 2 + 3

Input utente

Il programma può interagire con l'utente stampando e leggendo i caratteri sul terminale.
Vi sono diversi modi per farlo. In questo caso utilizzeremo la classe Scanner.

Codice

import java.util.Random;
import java.util.Scanner;

// InverseCalculator.java

public class InverseCalculator {
    private static char getRandomOp() {
        Random random = new Random();
        int op = random.nextInt(4);

        switch (op) {
            case 0:
                return '+';
            case 1:
                return '-';
            case 2:
                return 'x';
            case 3:
                return '/';
            default:
                return 'e';
        }
    }

    private static int calculateResult(int a, int b, char op) {
        switch (op) {
            case '+':
                return a + b;
            case '-':
                return a - b;
            case 'x':
                return a * b;
            case '/':
                return a / b;
            default:
                return 0;
        }
    }

    @SuppressWarnings("resource")
    private static int readAnswer() {
        Scanner scanner = new Scanner(System.in);
        int answer = scanner.nextInt();
        return answer;
    }

    public static void main(String[] args) {
        Random random = new Random();
        int a = random.nextInt(20);
        int b = random.nextInt(10);

        char op = getRandomOp();
        int result = calculateResult(a, b, op);

        System.out.print("What is " + a + " " + op + " " + b + "? ");

        // int answer = Integer.parseInt(System.console().readLine());
        int answer = readAnswer();

        if (answer == result) {
            System.out.println("Correct!");
        } else {
            System.out.println("Wrong!");
        }
    }
}

Compilazione ed esecuzione

javac InverseCalculator.java
java InverseCalculator

Leggere e scrivere su file

Leggere e scrivere su file è un'altra operazione estremamente frequente in un programma.

Per leggere e scrivere su file si utilizzerà la classe FileReader e FileWriter.

Codice

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

// WordCounter.java

public class WordCounter {
    private static int readWords(String file) {
        try (FileReader fileReader = new FileReader(file)) {
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line;
            int wordCount = 0;
            while ((line = bufferedReader.readLine()) != null) {
                String[] words = line.split(" ");
                wordCount += words.length;
            }
            bufferedReader.close();
            return wordCount;
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        return 0;
    }

    public static void writeCount(String file, int count) {
        try (FileWriter fileWriter = new FileWriter(file)) {
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            PrintWriter printWriter = new PrintWriter(bufferedWriter);
            printWriter.println(count);
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        if (args.length != 2) {
            System.err.println("Usage: java WordCounter <input_file> <output_file>");
            System.exit(1);
        }

        String inputFile = args[0];
        String outputFile = args[1];
        int wordCount = readWords(inputFile);
        writeCount(outputFile, wordCount);
    }
}

Compilazione ed esecuzione

javac WordCount.java
java WordCount.java input.txt output.txt

Challenge

  • Scrivere un Hello World in maniera più creativa
  • Scrivere un programma che calcoli l'area di un cerchio
  • Individuare il bug che affligge il programma di calcolo inverso
  • Rendere più interattivo il programma di calcolo inverso, chiedendo all'utente di rispondere più volte e parametrizzandolo da riga di comando
  • Scrivere un programma che conti il numero di occorrenze di una specifica parola in un numero arbitrario di file di testo