Elementi di base del linguaggio



Scaricare 325.66 Kb.
Pagina1/4
12.11.2018
Dimensione del file325.66 Kb.
  1   2   3   4

Introduzione
Il Java è un linguaggio di programmazione orientato agli oggetti (OO: Object Oriented), studiato e realizzato dall’azienda statunitense Sun Microsystem (spesso semplicemente Sun) negli anni ’90 dello scorso secolo.

Il Java è un linguaggio object oriented (OOL: Object Oriented Language) di tipo “puro”, perché è stato sviluppato appositamente per implementare (realizzare) in un progetto software tutte le caratteristiche della programmazione orientata agli oggetti (OOP: Object Oriented Programming). Spesso per “Java” non si intende soltanto il linguaggio di programmazione, ma anche la tecnologia fondata sull’insieme delle tecniche alla cui base vi è il linguaggio Java.

Per comprendere meglio le caratteristiche del Java, analizziamo le motivazioni e il quadro storico-scientifico che ne hanno determinato lo sviluppo.
Breve storia del linguaggio

Le origini del Java derivano da quelle del linguaggio di programmazione C++ che discende a sua volta dal C. Il C è un linguaggio di programmazione sviluppato nel 1972 da Dennis Ritchie, ricercatore nei laboratori Bell dell’azienda AT&T negli Stati Uniti, con l’obiettivo di utilizzarlo per completare il codice sorgente del sistema operativo UNIX. Il C segue il paradigma (modello) procedurale con alcune funzioni per il controllo dell’hardware, tipiche dei sistemi operativi. Il C++ nasce ufficialmente nel 1983 da un’idea di Bjarne Stroustrup, ricercatore presso gli stessi laboratori di Ritchie. Stroustrup, dovendo realizzare software molto complessi, completo il linguaggio aggiungendogli i costrutti tipici dell’OOP. Il C++ è quindi un OOL ibrido perché permette ai programmatori di realizzare progetti software seguendo il paradigma procedurale e/o quello orientato agli oggetti. Il C++ è quindi una versione del linguaggio C incrementata dai costrutti dell’OOP, come sottolinea il carattere speciale ++ (due + scritti in sequenza, spesso letti in inglese plus plus) che in C (così come in Java) rappresenta un operatore speciale la cui funzione è quella di incrementare di una unità il valore di una variabile.

Il linguaggio Java nasce ufficialmente il 23 maggio del 1995 in una presentazione organizzata da John Gage, direttore della divisione informatica della Sun Microsystem, e Marc Andreessen, vice presidente della Netscape. Java fu presentato come una nuova tecnologia (insieme di tecniche) software che stava per essere incorporata in Netscape Navigator, che in passato rappresentava uno dei più diffusi programmi per la navigazione (browser) nel World Wide Web (o semplicemente Web) della rete mondiale di computer Internet.

Lo sviluppo di Java aveva però avuto inizio nel dicembre del 1991 presso i laboratori della Sun da un progetto di ricerca denominato the Green project (letteralmente “il progetto verde”). Il gruppo di ricercatori del Green project aveva avuto l’incarico di studiare un nuovo linguaggio, adatto per controllare apparecchiature elettroniche di largo consumo (in particolare per la televisione via cavo negli Stati Uniti) e computer “tascabili”, indicati con la sigla PDA (Personal Digital Assistants), in grado di funzionare senza fili (wireless). Il Green project scarto l’impiego del C, perché si sarebbe dovuta sviluppare una versione diversa del codice del programma per ogni tipo di hardware e software di base. I ricercatori partirono quindi dalle basi del C++, cercando però di modificarlo per renderlo, da un lato, il più semplice possibile e, dall’altro, adatto alla creazione di progetti, anche complessi, in grado di essere eseguiti su macchine differenti. Il Java eredita comunque dal C e dal C++ la caratteristica di essere un linguaggio nato da programmatori per altri programmatori con l’obiettivo primario di rendere più semplice l’attività di codifica, in particolare di programmi complessi. Il gruppo Green project sviluppo quindi la prima versione del linguaggio, denominata Oak (letteralmente: quercia), con la caratteristica di essere indipendente dalla piattaforma o multi-piattaforma (cross platform); un programma poteva quindi essere eseguito su una qualsiasi macchina reale in modo indipendente dall’hardware (famiglia di microprocessori) e dal sistema operativo.

Nel seguito, seguendo la terminologia della Sun, per piattaforma intenderemo un elaboratore individuato da un particolare sistema operativo e dall’hardware, il cui funzionamento è determinato essenzialmente dal tipo di linguaggio macchina proprio di una specifica famiglia di microprocessori.

Terminata l’esperienza del Green project, il nome del linguaggio fu modificato nel 1995 in Java, che nello slang USA significa caffè (da cui deriva il logo ufficiale del linguaggio della Sun). Nei primi anni ’90, l’attenzione internazionale era però focalizzata sulla diffusione del World Wide Web in Internet, per cui i ricercatori della Sun capirono che il nuovo linguaggio, indipendente dalla piattaforma, sarebbe stato ideale anche per il Web.

Rimane nella storia del Java il 1995, quando James Gosling presentò la prima pagina per il Web interattiva in una conferenza internazionale a Monterey al centro della Silicon-Valley in California. La pagina includeva l’animazione di una molecola, realizzata con un’applicazione Java, che ruotava su se stessa quando Gosling muoveva il puntatore del mouse sullo schermo del computer. Le pagine per il Web, che fino a allora includevano esclusivamente testo e immagini fisse, potevano essere rese dinamiche e interattive inserendo dei programmi Java di piccole dimensioni (limitato numero di byte) e indipendenti dalla piattaforma (hardware del computer e browser), denominati applet.

Dopo il successo del lancio del Java nel 1995, la Sun mise a disposizione, in uno dei suoi siti Web in Internet, la possibilità di effettuare gratuitamente il download dell’ambiente per lo sviluppo di programmi e applet Java, negli hard disk locali di chiunque fosse interessato a utilizzare questa nuova tecnologia.


Bytecode e la macchina virtuale Java

Il compilatore Java traduce automaticamente un codice sorgente in un nuovo programma formato da una sequenza di bytecode (letteralmente “codice a byte”). Un bytecode è un’istruzione eseguibile da una macchina virtuale Java (JVM: Java Virtual Machine) programmabile.

Nel caso di molti linguaggi, tra cui il C/C++, il compilatore traduce un programma sorgente in un codice oggetto scritto con il linguaggio macchina specifico di una famiglia di CPU. Possiamo pensare al codice oggetto come a un sottoprogramma che, per formare il programma finale eseguibile, deve essere collegato (linking) con altri sottoprogrammi (forniti dal sistema operativo, dal compilatore e/o dallo stesso programmatore), mediante uno strumento software denominato linker, disponibile negli IDE per la programmazione. Il programma eseguibile finale può quindi essere eseguito da una CPU reale, dopo essere stato trasferito (allocato) nella memoria centrale (in genere una RAM). Un programma eseguibile scritto con il linguaggio macchina di una famiglia di CPU, è anche denominato codice nativo (native code).

Nel caso del Java, il compilatore traduce un programma sorgente in un codice “oggetto” scritto con istruzioni bytecode “eseguibili” mediante una macchina virtuale programmabile (la JVM), che quindi non esiste nella realtà. Il compito di trasformare il bytecode nel codice nativo e di eseguirlo in un computer reale è invece affidato all’interprete Java specifico di ogni tipo di piattaforma. La figura seguente pone a confronto il processo di compilazione di programmi sorgente C/C++ e Java, con la successiva esecuzione del codice oggetto in un computer reale (caso C/C++) e la realizzazione della macchina virtuale Java in una specifica piattaforma (caso Java).


Esecuzione

Programma sorgente C/C++

Programma oggetto in codice nativo

Compilatore C/C++

Linker

Programma finale eseguibile



Computer reale:

CPU + sistema operativo

Librerie di sottoprogrammi oggetto

RAM reale

Programma sorgente Java

Programma oggetto in bytecode

Compilatore Java

Esecuzione

Interprete Java

Programma finale eseguibile

Computer reale:

CPU + sistema operativo

RAM reale

Realizzazione della macchina virtuale Java in una specifica piattaforma

L’azione di un interprete Java è diversa da quella dei software interpreti di altri linguaggi; quest’ultimi traducono in linguaggio macchina direttamente le istruzioni ad alto livello del programma sorgente, mentre un interprete Java traduce in linguaggio macchina dei comandi che sono già una trasformazione del programma sorgente in una forma più vicina alla macchina, anche se virtuale. Una JVM è un processore programmabile, svincolato da qualsiasi tecnologia hardware e software, che la Sun definisce in modo standard mediante un’architettura ideale i cui elementi sono descritti nella figura seguente.
Macchina virtuale Java (JVM) programmabile

Unità di input virtuale

Unità di output virtuale

Flussi di input

Flussi di output

CPU virtuale

Insieme di registri
Unità di decodifica del set di istruzioni (bytecode) che definisce il linguaggio macchina della JVM

Bus degli indirizzi virtuale

Bus dei dati virtuale

Memoria centrale virtuale

Area di memoria per l’allocazione del codice in bytecode

Zona riservata ai dati

Zona riservata allo stack

Zona riservata all’heap

Una macchina virtuale Java è quindi realizzata in ogni specifica piattaforma mediante un differente interprete del linguaggio che esegue bytecode per un computer reale. Un programma in bytecode è indipendente dalla piattaforma, dovendo essere “eseguito” dalla medesima JVM definita dalla Sun per qualsiasi computer.

Per essere sempre disponibile, il codice sorgente Java deve essere memorizzato in un file (denominato file sorgente) di estensione .java. Il compilatore Java accetta quindi in ingresso un file .java e produce un nuovo file di estensione .class con comandi in bytecode. Un file .java e portabile su qualsiasi computer per cui produrrà sempre, dopo la sua compilazione, il medesimo file .class con il bytecode. Al contrario, un compilatore Java è un programma eseguibile, per cui il suo codice nativo sarà differente e specifico di ogni singola piattaforma, anche se qualsiasi traduttore Java implementerà sempre i medesimi algoritmi per tradurre i codici sorgente in quelli in bytecode per la JVM.
Esecuzione del bytecode

L’esecuzione di un programma in bytecode nell’ambiente run time Java avviene, nell’ordine, in tre fasi, schematizzate nella tabella e nella figura che seguono. Ognuna delle tre fasi per l’esecuzione del bytecode è realizzata, in modo automatico e “invisibile” per gli operatori, mediante uno strumento software inserito nello stesso JRE.




Fase di esecuzione di un programma

Descrizione

  1. Caricamento del programma in bytecode, eseguito dal caricatore di classi (class loader)

Il JRE legge il programma in bytecode (memorizzato in un file .class su una memoria di massa) per verificare quali altri elementi software (classi) sono necessari per l’esecuzione del programma e quindi determinare lo spazio totale necessario nella memoria centrale (RAM) del computer. Il JRE richiama quindi il class loader che trasferisce il codice del programma da eseguire (a sua volta una classe) e quello delle classi necessarie (sempre in bytecode) in una zona della RAM. In questa fase, il caricatore di classi riserva una zona della memoria centrale per l’esecuzione del programma, impedendo che altre applicazioni possano modificare il bytecode. Questa azione garantisce la sicurezza del codice, impedendo l’attacco di programmi non autorizzati, quali a esempio i virus.

  1. Controllo del codice, effettuato dal verificatore del bytecode (bytecode verifier)

Il verificatore del bytecode analizza il codice nella RAM, pronto per essere eseguito, per controllare che il programma non effettui alcuna delle “azioni” proibite definite nelle specifiche della macchina virtuale Java. Questa fase è diversa nelle applicazioni rispetto alle applet, dove le regole di sicurezza sono molto più restrittive. Nel caso di violazione di una delle regole di sicurezza della JVM, l’esecuzione del programma si blocca con un errore run time.

  1. Esecuzione del bytecode mediante l’interprete run time (run time interpreter)

L’interprete run time legge, in sequenza, un’istruzione bytecode dopo l’altra nella RAM, la traduce in linguaggio macchina e la esegue nella piattaforma reale. Gli errori run time in questa fase dovrebbero essere limitati a quelli commessi dagli utenti, perché gli altri errori sono già stati eliminati dal verificatore del bytecode


Programma in bytecode (definito da una classe)

Programma.class

Lettura del programma in bytecode

Calcolo della dimensione della zona della RAM necessaria per l’allocazione del programma

Allocazione nella RAM del programma in bytecode (una classe) e delle altre classi necessarie per l’esecuzione del programma stesso.

Programma in bytecode

Bytecode delle classi richiamate

RAM


Librerie di classi

1. Caricatore di classi

Controllo del bytecode nella RAM



2. Verificatore del bytecode

Il programma effettua qualche azione non permessa dalla JVM?

Errore run time:

termine esecuzione del programma



TRUE

FALSE

3. Interprete run time

Ultima istruzione?



TRUE

FALSE

Termine esecuzione del programma senza errori

Traduzione di una istruzione bytecode in codice nativo

Esecuzione codice nativo della CPU

Errori di esecuzione?

TRUE

Errore run time:

termine esecuzione del programma

FALSE

L’interpretazione di un programma in bytecode è più lenta dell’esecuzione di un programma compilato in codice nativo. I programmatori della Sun hanno però ridotto i tempi per l’interpretazione del bytecode con la distribuzione delle nuove versioni dell’JDK/JRE agendo sui seguenti fattori.



  • Miglioramento di alcuni costrutti del linguaggio con l’obiettivo di rendere più semplice il bytecode generato e quindi facile da tradurre nel codice macchina degli interpreti run time.

  • Gestione automatica della memoria da parte della JVM.

  • Ottimizzazione del codice nativo con cui sono scritte le diverse versioni del JRE.


Struttura di un programma sorgente Java

La struttura di un programma sorgente Java è descritta nel seguente diagramma sintattico.


Struttura di un programma sorgente Java

Dichiarazione di un package

import

Package


Nome classe

Dichiarazione di una classe

;

Il grafo precedente suggerisce come un programma Java sia formato da un insieme di una oppure più classi. Al nostro livello di studio, possiamo pensare a una classe come a un “contenitore” software che definisce gli attributi (proprietà o caratteristiche) e l’elaborazione (o comportamento) del programma che vogliamo realizzare. Gli attributi sono definiti mediante variabili, mentre l’elaborazione è realizzata dichiarando dei sottoprogrammi, denominati metodi, come rappresentato nel seguente schema (nel gergo dei programmatori “scheletro”) di codice sorgente che descriva l’organizzazione di una generica classe Java.


public class NomeClasse [extends NomeSuperclasse]optional [implements NomeInterfaccia]optional {

[repeat]optional

[repeat]optional

}
La parola chiave public indica che la classe è pubblica, per cui può essere utilizzata da qualsiasi classe esterna. La parola chiave extends permette di estendere gli attributi e il comportamento della classe in fase di progettazione, aggiungendo le variabili e l’elaborazione di un’altra classe, denominata superclasse. Questo meccanismo, tipico dell’OOP, che studieremo in seguito, è denominato ereditarietà. La parola chiave implements dichiara invece che una classe deve realizzare il comportamento di un’altra classe speciale, denominata interfaccia.

Il linguaggio Java distingue le lettere maiuscole da quelle minuscole per cui la parola chiave class e differente da CLASS oppure Class, che quindi non saranno riconosciute dal compilatore. Nel programma sorgente le istruzioni sono separate da un punto e virgola e ogni comando può essere scritto con un formato libero in più linee. Per migliorare l’ordine e la chiarezza del codice sorgente, è però preferibile scrivere il programma in modo ordinato con una istruzione per ogni linea.

Per utilizzare una classe in un programma, si deve definire una copia (o istanza) dei suoi attributi, che nella programmazione orientata agli oggetti viene denominata oggetto. Un’istanza, o oggetto di una classe, permette anche di richiamare i metodi introdotti dal programmatore durante la dichiarazione della classe.

Il comando import, con cui può iniziare un programma Java, segnala al compilatore la classe o l’insieme delle classi da includere. Il codice in bytecode delle classi sarà poi effettivamente incluso nel programma dal JRE al momento dell’esecuzione del programma. Una delle caratteristiche principali del Java (e dell’OOP in generale) è la riusabilità dei programmi realizzati. Il riuso del software avviene mediante le classi che, una volta definite dai programmatori, possono essere riutilizzate in altri programmi semplicemente richiamando le loro funzionalità mediante il comando import. La stessa tecnologia Java è definita mediante un insieme di classi di base, che formano il Java Foundation Classes (JFC). Le classi “fondamenta” del Java sono memorizzate nell’JDK, nella sottocartelle contenute nei percorsi C:\Programmi\java\jre-x.x.x\lib e C:\Programmi\java\jdk-x\lib.

Nella tecnologia Java le classi sono organizzate in gruppi di classi (in bytecode), denominati package, ognuno dei quali è dedicato alla soluzione di una specifica categoria di problemi. Un gruppo di classi di un package può essere considerato dai programmatori come una libreria di classi. La tabella seguente descrive le funzioni dei package principali della JFC, che impareremo a studiare durante lo studio del linguaggio.


Package della JFC

Contiene l’insieme delle classi che definiscono ...

java.lang

... le funzionalità elementari del linguaggio quali, a esempio, la definizione dei tipi di dato per le variabili (numeri interi, reali, caratteri ecc.) e delle operazioni matematiche più frequenti.

java.util

... alcuni comandi utili per i programmatori per la definizione delle date e delle ore, la gestione degli array o la creazione di modelli dei dati complessi.

java.io

... le operazioni di input/output principali.

java.applet

... il comportamento delle applet di Java 1.

java.awt, javax.swing,

... i comandi principali per costruire l’interfaccia utente grafica di applicazioni e delle applet Java 2

java.net

... alcuni comandi per stabilire il collegamento con altri calcolatori inseriti in una rete di computer.

Per includere una specifica classe contenuta in un package si usa il comando import con la forma



import nome_package.nomeClasse;

mentre per includere tutte le classi di una libreria si deve modificare la sintassi precedente nel modo seguente:



import nome_package.*;

Tra i package fa eccezione quello java.lang, che è automaticamente inserito dal compilatore Java. L’istruzione:



import java.lang.*;

inserita prima della dichiarazione di una nuova classe è quindi ridondante, perché è aggiunta automaticamente dal compilatore prima di iniziare la traduzione del codice sorgente.

Un comando del tipo import nome_package.* rallenta l’attività del compilatore Java che, prima di iniziare la traduzione, deve caricare nella memoria centrale (RAM) tutte le classi di un package. In ogni caso, il compilatore utilizzerà soltanto le classi che sono richiamate nel codice sorgente del programma. L’alternativa all’inclusione di interi package è quella di inserire un lungo elenco di comandi import seguiti dai nomi delle classi specifiche.

I programmatori Java preferiscono l’istruzione import nome_package.* per aumentare la chiarezza del codice, accettando un intervallo di tempo aggiuntivo per la compilazione, che nei computer con buone prestazioni (CPU e RAM) è spesso trascurabile.


Applicazioni stand-alone console

Un’applicazione stand-alone console, o semplicemente applicazione console, è eseguita con un’interfaccia di tipo testuale in cui le operazioni di input avvengono mediante la tastiera mentre l’output è visualizzato in forma di testo sullo schermo dell’unità video.

Nei sistemi operativi basati su un’interfaccia utente grafica, le operazioni sulla console (tastiera più unità video) sono effettuate in una finestra in cui compare soltanto testo.

La struttura di un’applicazione console è descritta mediante il seguente scheletro generale di codice sorgente.




Condividi con i tuoi amici:
  1   2   3   4


©astratto.info 2019
invia messaggio

    Pagina principale