Visualizzazione post con etichetta j2se. Mostra tutti i post
Visualizzazione post con etichetta j2se. Mostra tutti i post

sabato 14 febbraio 2009

Java Reflection

Java Reflection

La reflection permette di esaminare o modificare il comportamento run-time di un applicazione java  eseguita nella JVM. Questa proprietà avanzata è applicata, tra l'altro, nei seguenti casi:
  • Caratteristiche di estensibilita: un'applicazione può fare uso di classi esterne definite dall'utente, creando istanze si esse usando il "fully-qualified" path.
  • Ambienti di sviluppo visuale (IDE): generalmente gli ambienti di sviluppo visuale (p.e. NetBeans o Eclipse), che richiedono la presenza di un 'browser' di classi,  per aiutare gli sviluppatori a scrivere codice corretto e semplice fanno largo uso della reflection, molto utile quando di vuole enumerare i membri di una classe.
  • Debug e test: i debugger necessitano di conoscere i membri privati di una classe, per determinarne il valore in un determinato istante.

La reflection è uno strumento molto potente ma che dovrebbe essere usato con cautela; in genere se è possibile raggiungere un obiettivo senza l'uso della reflection, è preferibile non farne uso. In particolare è necessario tener presente i seguenti svantaggi:
  • Overhead delle prestazioni: considerando che la reflection comporta la risoluzione dei tipi dinamicamente, alcune ottimizzazioni fatte dalla JVM non possono essere effettuate, per cui l'uso della reflection è sconsigliato nelle sezioni di codice che sono richiamate frequentemente nelle applicazioni performance-sensitive.
  • Restrizioni di sicurezza: la reflection richiede permessi di sicurezza che non sono presenti quando l'esecuzione di un applicazione avviene in presenza di un security manager. Di questo particolare bisogna tener conto per esempio nello sviluppo di un'Applet.
  • Esposizione dei membri interni: considerando che la reflection permette di effettuare operazioni 'illegali', per esempio accesso ai campi e metodi privati di un oggetto, si potrebbe rischiare di compromettere la portabilità del codice. In particolare viene eliminato il concetto di astrazione con conseguenti problemi in seguito ad upgrade della piattaforma.

Attraverso la reflection è possibile conoscere a run-time tutti i metodi di una classe ed eventualmente invocarne uno. Il seguente listato permette di invocare il metodo di una classe con opportuni parametri, senza che siano noti a priori il numero e tipo dei parametri:


package test;

import java.lang.reflect.*;
import java.util.Hashtable;

public class ReflectionTest {

    public static void main(String args[])
            throws Exception {
        Class classe = Class.forName(args[0]);
        String methodName = args[1];
        Method[] methods = classe.getDeclaredMethods();
        Hashtable<String, Method> methodHashtable =
                new Hashtable<String, Method>();
        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            String name = method.getName();
            methodHashtable.put(name, method);
        }
        Method m = methodHashtable.get(methodName);
        Class[] paramTypes = m.getParameterTypes();
        if (paramTypes.length != args.length - 2) {
            System.out.println("Num. parametri errato");
            System.exit(0);
        }
        int paramNum = paramTypes.length;
        Object[] params = new Object[paramNum];
        for (int j = 0; j < paramNum; j++) {
            Class type = paramTypes[j];
            Constructor c =
                    type.getConstructor(String.class);
            params[j] = c.newInstance(args[j + 2]);
        }
        System.out.println(m.invoke(classe.newInstance(), params));
    }
}

class ReflectionTestClass {

    public Integer sum(Integer i, Integer j) {
        return i + j;
    }
}


Il main si richiama con la sintassi "java ReflectionTest <nomeClasse> <nomeMetodo> <parametro1> <parametro2> ...", per esempio "java test.ReflectionTest test.ReflectionTestClass  sum 2 3"

lunedì 26 gennaio 2009

Primi passi con java

Installazione
 Le seguenti istruzioni riguardano l'istallazione della JDK vesione 1.6.0_x(in seguito jdk1.6.0_<version>) su Windows:
  1. Scaricare la versione più aggiornata della Java SE Development Kit
  2. Eseguire l'istallazione attraverso il file jdk-6u1<version>-windows-i586-i.exe. Durante l'installazione è possibile confermare i valori default cliccando su "Next" e "Yes". Al termine dell'istallazione potrebbe essere richiesto di riavviare il sistema. Per verificare la corretta installazione è possibile controllare che l'albero delle directory abbia la seguente struttura:
  3. Aggiornare la variabile d'ambiente PATH (Opzionale): è possibille settare opzionalmente la variabile PATH se si vuole usare gli eseguibili della JDK (javac.exe, java.exe, javadoc.exe,...) a partire da qualsiasi directory senza specificarne ogni volta il path completo. Nel caso in cui la PATH non viene aggiornata, ogni volta che si vuole compilare un programma java è necessario, per esempio, eseguire quanto segue: 
C:> "\Program Files\Java\jdk1.6.0_<version>\bin\javac" MyClass.java
a meno che non ci si trovi nella cartella C:> Program Files\Java\jdk1.6.0_<version>\bin\. In genere, quando viene eseguito un comando, Windows verifica se tale comando "si trovi" in una delle directory specificate nella variabile PATH, per cui se il percorso della directory che contiene javac viene registrato nella variabile PATH è possibile eseguire semplicemente:
javac MyClass.java
ed evitare che cosi venga visualizzato il seguente messaggio:
"jevac" non è riconosciuto come comando interno o esterno, un programma eseguibile o un file batch.

Per settare la variabile è necessario
  1. cliccare Start > Pannello di Controllo > Sistema (su Windows XP) o Start > Setting > Pannello di Controllo > Sistema su Windows 2000
  2. cliccare Avanzate > Variabili d'Ambiente
  3. Settare nella variabile PATH delle 'Variabili Utente' il percorso della directory in cui si trovano gli eseguibili java.exe, javac.exe, ..., se il vecchio valore di PATH è non vuoto è necessario aggiungere ';' e poi il percorso della director, es.:
 'vecchio_valore_di_PATH; C:\Program Files\Java\jdk1.6.0_<version>\bin'
Oltre alla JDK è consigliabile (per principianti ed esperti) eseguire il download della documentazione delle API in formato html e tenerla sempre a portata di mano.




Il primo programma
Il primo passo per eseguire un programma java è quello di creare un file sorgente (per esempio MyFirstClass.java). Un file sorgente java è un file di testo con estensione .java creabile e modificabile utilizzando un qualsiasi editor di testi (es. Vi per Linux o NotePad per Windows).

public class MyFirstClass {
    public static void main(String[] args){
        System.out.println("Il primo programma!!!");
    }
}

Dopo aver salvato il file MyFirstClass.java in C:\corsojava\ (facendo attenzione alle maiuscole e minuscole, perchè Java è case-sensitive) la fase sucessiva prevede che il file sorgente venga dato in pasto al compilatore Java (attraverso il comando javac):

C:\corsojava> javac MyFirstClass.java

Il sorgente java è cosi tradotto in istruzioni in formato di basso livello universale(bytecode), eseguibili da qualsiasi Java Virtual Machine(JVM). Tali istruzioni vengono salvate in un file con estensiopne .class (MyFirstClass.class) è cosi possibile eseguire il primo programma.

C:\corsojava> java MyFirstClass

con il seguente risultato:


Il bytecode generato è indipendente dalla piattaforma HW/SW utilizzata, sarà compito della JWM tradurre il bytecode, una istruzione alla volta, in istruzioni macchina compatibili con la piattaforma utilizzata. Da questa importante proprieta di cui la programmazione java derivano i seguenti concetti:
  1. Portabilità: il codice Java sviluppato su una piattaforma può essere trasferito senza particolari problemi su un altra piattaforma (es. da Windows a Linux). Ciò è garantito quasi al 100% poichè le JVM sviluppate per i vari sistemi operativi hanno un comportamento pressochè uniforme. Tale uniformità di funzionamento non è tuttavia garantita per le JVM implementate per i dispositivi di telefonia mobile di ultima generazione che supportano java.
  2. Migrazione/mobilità: è possibile eseguire in locale codice java situato su siti remoti accessibili tramite la rete Internet caricandolo il in locale "al volo", senza preoccuparsi per esempio del S.O. su cui dovrà essere eseguito tale bytecode.

Si noti che l'interprete non prende in pasto un file (per l'esempio precedente non abbiamo eseguito il comando java MyFirstClass!) bensì il nome di una classe, che deve coincidere con il nome del file .class che contiene il bytecode della nostra classe.



Un programma che usa API standard

In genere un programma java fa uso di diverse classi (appartenenti alle API standard di java, definite dall'utente oppure appartenenti ad API non standard). Le classi sono raggruppate in librerie, chiamate package. In MyFirstClass abbiamo fatto uso della classe System, che fa parte del package java.lang. Tale package è importato implicitamente in ogni programma java per cui le classi in esso definite possono essere usate direttamente (come anche la classe String).
 Classi di package diversi da java.lang devono essere localizzate in maniera esplicita, importandole attraverso la direttiva di importazione la cui sintassi è la seguente:

import nome_completo_della_classe;

esempi di importazione sono

import java.util.Date;

oppure

import java.util.*;

quest'ultima istruzione permette di usare tutte le classi definite nel package java.util

Il seguente programma fa uso della classe Date definita nel package java.util e stampa l'ora corrente:

import java.util.*;
public class MySecondClass {
    public static void main(String[] args){
        /**la seguente istruzione usa
         *la classe Date definita in java.util
        * */
        System.out.println("Ora corrente: "+new Date());
    }
}

il risultato della sua esecuzione è:



Un programma che usa API non standard



Il bytecode di una classe contiene le istruzioni della classe stessa e gli eventuali riferimenti al codice di altre classi usate. Quest'ultime saranno usate dalla JVM per reperire il bytecode necessario durante l'esecuzione. Se in una classe si vuole usare un'altra classe non API è necessario, se quest'ultima non si trova all'interno della stessa directory della prima, importarla esplicitamente.
Java usa la variabile CLASSPATH, che contiene i pathname delle directory in cui si trovano i package non API. Tale variabile può essere settata allo stesso modo con cui viene settata la variabile PATH, vista in precedenza. Tuttavia la variabile CLASSPATH è tipica della JVM, a differenza di PATH che ha un uso più generico, per cui potrebbe essere necessario crearla ad hoc. Il contenuto della variabile CLASSPATH deve essere tipo:

.;C:\javalib\mio_package.jar

se in c:\javalib\mio_package.jar sono localizzati i file che definiscono il bytecode delle classi che useremo all'interno di una nuova classe. In alternativa è possibile usare, in fase di compilazione(javac) o esecuzione(java) il parametro -classpath nel seguente modo:


java -classpath c:\javalib\mio_package.jar MyThirdClass