A. Veneziani - Commento al programma elementare “Ciao mondo !” e al programma per effetturare la somma di due numeri letti in input Spiegazione abbinata al listato test_io.java public class test_io { public static void main(String[] args) { System.out.println("Ciao Mondo !"); } } Il listato presenta l’implementazione di una classe (test_io) dichiarata pubblica. Questa indicazione comporta che la classe stessa sia richiamabile anche al di fuori del package ove si trova. In questa classe è presente anche il metodo main(); tale metodo, se la classe ne è provvista, ossia se è presente nel listato, viene attivata ogni volta che la classe di avvio del progetto viene richiamata. Tale classe quindi dovrà sempre contenere un metodo main(). Una classe “di avvio” che non ha metodo main() produrrà perciò un errore. Si può anche osservare che il metodo main è dichiarato in questo programma public. Questo indica che esso è richiamabile da qualsivoglia punto di un altro programma, ossia che questo metodo è visibile (richiamabile) ovunque, senza limitazioni. Ciò si rende necessario, essendo lo stesso richiamato da un contesto esterno. Si tratta anche di un metodo statico (static). Questo vuol dire che questo metodo, per essere usato, non abbisogna che la classe relativa sia istanziata in un’oggetto. Void, indica invece che il metodo main non rende valori. Esistono per contro metodi che ne rendono. L’indicazione String[] args tra parentesi, indica la capacità del metodo main di rilevare in un apposito vettore di stringhe I valori (parametri) inseriti al seguito del nome della classe di avvio, al momento del lancio della applicazione Java. In genere quindi si effettuerà, da riga di comando (finestra “Prompt dei comandi” in Windows): c:\work\java\hello> Ciao Alberto ! java hello Alberto In questo caso abbiamo passato all’applicazione hello un parametro stringa (contente un nome di persona), che viene poi riutilizzato dal programma stesso, nello svolgimento del suo codice (produrre ad esempio un saluto personalizzato, ossia un saluto a quella determinata persona). Tipicamente, per fissare le idee riguardo su tali tipi di classi si può tenere presente la classe Math, i cui metodi (funzioni matematiche) sono tutti statici, ossia, in altre parole, per utilizzare tali metodi non è necessario istanziare un oggetto Math. Nessuno degli elementi sopra descritti del metodo main può essere tralasciato o alterato. Se ciò succedesse, siccome la chiamata effettuata dalla JVM è verso un metodo che ha caratteristiche ben precise, come quelle appunto indicate e non altre. Segue l’invocazione alla classe System. Esso, tra altre funzioni, è preposto a controllare l’ I/O sui tre flussi principali di dati, quali standard input, standard output e standard error (canale standard di uscita delle segnalazioni di errore). Essa non viene istanziata ed infatti in essa viene usato, un campo statico (variabile statica) di nome out. Tale variabile, essendo statica, esiste abbinata alla classe, non agli oggetti. Questa variabile, rappresenta nello specifico caso, un oggetto essa stessa, e specificamente un oggetto di tipo PrintStream. Gli oggetti di questo tipo aprono dei canali per la scrittura dei dati, in questo caso la scrittura su video (che è il dispositivo standard di output). La classe possiede opportuni metodi, tra cui i metodi print e pritnln (quest’ultimo aggiunge dopo aver scritto un ritorno a capo). Di questi metodi esistono varie versioni con tipo di parametro diverso da caso a caso. Un meccanismo automatico interno, proprio dei linguaggi ad oggetti, permette di applicare il metodo print adeguato, a seconda del tipo di dato presente come parametro. Una volta svolte le istruzioni indicate nel metodo main, se non sussistono altri elementi attivi nel programma, il programma termina automaticamente la sua esecuzione. Commento ad un listato con operazioni di Input /* A. Veneziani - Programma che effettua I/O per calcolare una semplice somma di interi (Compilato con JDK 1.4.2) */ package io; import java.io.*; public class test_io2 { public static void main(String[] args) { InputStreamReader Isr; BufferedReader Bfr; String s1, s2; int x, y, r; // definisce come sorgente del flusso di input lo "standard input" Isr = new InputStreamReader(System.in); Bfr = new BufferedReader(Isr); s1 = ""; s2 = ""; try { System.out.print("Leggi primo addendo: "); s1 = Bfr.readLine(); System.out.print("Leggi secondo addendo: "); s2 = Bfr.readLine(); } catch (IOException ioe) { }; x = Integer.valueOf(s1).intValue(); y = Integer.valueOf(s2).intValue(); r = x + y; System.out.println("Il risultato e': " + r); } } Il programma che andiamo ad analizzare si pone come scopo di sommare due numeri interi dopo averli letti da console. In questo listato vengono utilizzate 2 nuovi classi per realizzare l’input di dati (su una console a carattere), ossia InputStreamReader e BufferedReader. In effetti la classe BufferedReader è solo di supporto all’attività di lettura, ossia la facilita e la potenzia, aggiungendo un buffer di lettura, come il suo nome del resto dice. La lettura di se per se potrebbe essere effettuata anche con la sola classe InputStreamReader, operando carattere, per carattere, ed effettuando opportuni cicli di lettura. Al momento dell’istanza di InputStreamReader è necessario indicare l’origine dei dati in lettura: Isr = new InputStreamReader(System.in); In questo caso il flusso deriva da System.in, ossia, per quanto detto sopra per il listato precedente, dallo standard input (la variabile in infatti è un oggetto statico proprio della classe System). Questo canale di input, rappresentato dall’oggetto Isr, viene dotato di buffer, ossia di capacità di leggere gruppi di caratteri o dati cumulandoli, ossia tenendone memoria. Bfr = new BufferedReader(Isr); Dopo aver fatto questo, questi canali di input bufferizzati, possono leggere dati multipli, senza doversi appoggiare a cicli di lettura, ma operando in modo analogo a quello di altri linguaggi di programmazione Read in Pascal, Input in QuickBasic, ecc. Le operazioni di questo tipo in Java vengono svolte con s1 = Queste operazioni in Java devono avere però essere abbinate obbligatoriamente ad un opportuno controllo di errore. Questo è visibile poco sotto. Il costrutto try… catch, che successivamente sarà analizzato più a fondo, serve a controllare se le operazioni di input hanno avuto problemi o no, nella lettura dei dati. La gestione di questi problemi riguarda direttamente le operazioni in quanto tali, non la successiva gestione dei dati ed il loro uso. Il metodo readLine() legge tutto come una stringa (sequenza di caratteri) e quindi, in quanto tale, è molto generale nel suo uso. Successivamente il programma in questione deve convertire i dati letti in un formato meno generico, ossia intero nel nostro caso. La conversione in questo caso risulta piuttosto complessa e laboriosa Bfr.readLine(); x = Integer.valueOf(s1).intValue(); ovviamente x è una variabile dichiarata intera. Il valore s1 che è una stringa (dal contenuto opportuno), viene in questo modo convertito in numero intero a tutti gli effetti. Tali conversioni possono fallire, nel caso i dati non siano coerenti con il tipo previsto e quindi non convertibili. Nel programma in questione si suppone un input adeguato e non è stato previsto un controllo di errore sulle conversioni. Infine viene effettuata la somma, che non ha una sintassi particolare, né diversa da altri linguaggi: r = x + y; Infine si ricava il risultato tramite il solito costrutto di output che usa la classe System: System.out.println("Il risultato e': " + r); Da notare che, sebbene Java non effettui di regola conversioni implicitamente, in questo caso la concatenazione di r con la stringa costante che la precede è una operazione di conversione implicita del valore di r in stringa.