Caratteristiche, similitudini e differenze tra .NET e Java per lo sviluppo software

Caratteristiche, similitudini e differenze tra .NET e Java per lo sviluppo software

Piattaforme

Le piattaforme software principali che possono essere utilizzate per lo sviluppo di applicazioni web sono due: la piattaforma .NET e la piattaforma Java (Java Platform). In questo articolo informatico descriviamo le principali caratteristiche e differenza per queste due importanti piattaforme di sviluppo.

Caratteristiche, similitudini e differenze tra .NET e Java per lo sviluppo software

.NET

.NET è una piattaforma sviluppata da Microsoft principalmente per sistemi operativi Windows. All’interno di questa piattaforma agisce una grossa libreria di classi che prende il nome di Framework Class Library (FCL), di cui una delle caratteristiche principali è che ognuno dei linguaggi messi a disposizione può utilizzare il codice scritto in uno degli altri linguaggi che appartengono a .NET. Le applicazioni scritte per questa piattaforma vengono eseguite nel contesto di una virtual machine, il Common Language Runtime (CLR), che offre meccanismi per la gestione della sicurezza, della memoria e delle eccezioni. Il codice scritto in .NET prende il nome di “codice gestito” proprio per questo motivo. FCL e CLR assieme rappresentano il cuore della piattaforma .NET.
All’interno della libreria FCL sono presenti classi per la gestione della user interface, dell’accesso ai dati, della connessione col database, dello sviluppo di applicazioni web e molto altro. Quindi il codice scritto dal programmatore si va ad aggiungere a quello già offerto dalla piattaforma e eventualmente a quello fornito da altre librerie incluse dallo sviluppatore stesso.
La piattaforma .NET è accompagnata da una famiglia di piattaforme destinate per esempio alla programmazione mobile, ai sistemi embedded o a sistemi operativi alternativi.

Common Language Infrastructure

L’architettura della piattaforma è costituita da diversi elementi. Il primo di questi è la Common Language Infrastructure (CLI) che fornisce un contesto indipendente dal linguaggio utilizzato per lo sviluppo dell’applicazione, comprendente delle funzioni per la gestione del garbage collector, della sicurezza e della gestione delle eccezioni. Questo vuol dire che queste funzioni sono disponibili per tutti i linguaggi che sono supportati dalla piattaforma .NET. Il Common Language Runtime, che implementa la CLI, rappresenta il motore di esecuzione della piattaforma .NET. Quindi tutte le applicazioni sono eseguite sotto il controllo del CLR.

Il codice delle app potrebbe essere scritto in uno qualsiasi dei linguaggi .NET (come C#, VB o J#), ma qualsiasi sia il linguaggio utilizzato il codice viene compilato in un linguaggio indipendente dalla piattaforma hardware che prende il nome di CIL (Common Intermediate Language). Al momento dell’esecuzione il CLR utilizza un compilatore just-in-time (JIT) che trasforma il CIL in un codice macchina compatibile con la piattaforma hardware utilizzata. Questo vuol dire che a partire dallo stesso CIL si possono utilizzare diversi compilatori JIT per produrre codici in linguaggio macchina destinati a processori diversi.

Il codice eseguibile generato dal compilatore just-in-time viene salvato in quella che prende il nome di .NET Native Image Cache. Ciò permette di velocizzare i lanci dell’applicazione successivi al primo, che invece è solitamente più lento. Per rendere più veloce anche il primo lancio Microsoft ha messo a disposizione degli sviluppatori l’utility Native Image Generator, che permette di effettuare manualmente la compilazione ahead-of-time (AOT).

Indipendenza dal linguaggio e sicurezza dei tipi

Tutti i possibili tipi di dato e i costrutti di programmazione supportati dal CLR assieme alle loro possibili interazioni sono definite all’interno del Common Type System (CTS). Questo permette alle librerie e alle applicazioni scritte utilizzando uno dei linguaggi .NET di scambiarsi tipi e istanze di oggetti.
Sia il CTS che il CLR si occupano di assicurare la sicurezza dei tipi non permettendo cast mal definiti, invocazioni di metodi errate ed eventuali problemi di memoria durante gli accessi ad un oggetto.

Portabilità

Per quanto riguarda la portabilità, Microsoft ha implementato la piattaforma solo sui propri sistemi Windows. Il framework è stato però progettato in modo da poter essere sviluppato anche su altre piattaforme. Infatti, Microsoft ha sottomesso le specifiche del CLI (CTS, CIL, BCL e FCL) e C# sia all’ECMA che all’ISO. In questo modo il framework e i suoi linguaggi possono essere implementati anche su altre piattaforme.

Sicurezza

Il framework .NET utilizza il Code Access Security (CAS) per evitare che del codice non fidato possa effettuare delle azioni privilegiate. Nel momento in cui il CLR deve effettuare il caricamento di un assembly otterrà quella che prende il nome di evidenza, che è associata allo specifico assembly. Tramite questa evidenza, il CLR associa l’assembly ad un code group che a sua volta, rappresenta un insieme di permessi. Il codice che necessita di eseguire delle azioni con dei privilegi effettua un’opportuna richiesta. A questo punto, il CLR ripercorre lo stack delle chiamate per verificare l’insieme dei permessi associati ad ogni metodo nello stack: se anche uno solo degli assembly non possiede il permesso richiesto allora viene sollevata un’eccezione di sicurezza.
Sempre restando in tema di sicurezza, il bytecode del CIL se non è offuscato può essere reverse- engineered in modo semplice. All’inizio degli anni 2000 Microsoft ha introdotto Dotfuscator, per evitare che venga generato del codice intermedio non offuscato.

Gestione della memoria

Per quanto riguarda la gestione della memoria, lo sviluppatore non si deve preoccupare né di allocare la memoria né di rilasciarla. Il CLR si fa carico di questo lavoro e capisce autonomamente quando la memoria può essere rilasciata in sicurezza. Gli oggetti che vengono istanziati in .NET ricevono una porzione di memoria da quello che prende il nome di managed heap, che è gestito dal CLR. Fintantoché esiste un riferimento associato ad un oggetto, allora quest’ultimo non può essere eleggibile per la cancellazione; dal momento in cui l’oggetto non è più accessibile perché nessun riferimento punta ad esso, allora la memoria allocata ad esso potrà essere deallocata dal garbage collector (GC).

Il garbage collector viene eseguito periodicamente su un thread diverso da quello dell’applicazione e controlla l’eventuale presenza di oggetti non più accessibili in modo da liberare la memoria che era stata allocata per essi. Il GC è non deterministico perché entra in esecuzione solo quando la quantità di memoria utilizzata ha superato una certa soglia o quando il sistema ha bisogno di memoria libera. Ogni applicazione ha un insieme di root, che sono dei puntatori agli oggetti del managed heap. Tra questi ci sono i riferimenti agli oggetti statici e a quelli definiti come variabili locali o parametri dei metodi nello scope corrente. Nel momento in cui il GC entra in esecuzione l’applicazione viene messa in pausa e il garbage collector contrassegna come raggiungibili tutti gli oggetti puntati dall’insieme di root ed eventuali altri oggetti in essi incapsulati. Poiché l’heap era stato precedentemente allocato in modo contiguo, tutti gli oggetti che non sono stati contrassegnati diventano garbage, il che vuol dire che la memoria che era stata allocata per essi è diventata spazio libero. Questa operazione prende il nome di mark. A questo punto, dato che ci potrebbero essere blocchi di memoria libera tra gli oggetti che erano stati allocati inizialmente, viene eseguita un’operazione di sweep che serve a compattare la memoria. Al termine dell’esecuzione del GC l’applicazione può riprendere l’esecuzione, anche se nelle ultime versioni della piattaforma .NET queste pause sono impercettibili perché il garbage collector è eseguito in background.

Un’altra caratteristica del garbage collector è che esso è generazionale perché ad ogni oggetto viene assegnata una generazione. Gli oggetti creati per ultimi rappresentano la Generazione 0; quelli che resistono ad un’operazione di garbage collection diventano Generazione 1; gli oggetti di Generazione 1 che sopravvivono ad un’altra garbage collection sono contrassegnati come Generazione 2. Avere un elevato valore di generazione vuol dire essere soggetti a meno operazioni di garbage collection. Questo permette di migliorare l’efficienza del GC perché un numero minore di oggetti necessità di essere compattato.

Java

Java è una piattaforma software sviluppata originariamente da Sun Microsystem, che è stata poi acquisita da Oracle. Con l’utilizzo di Java è possibile sviluppare applicazioni indipendenti dall’hardware di esecuzione. Questo infatti è virtualizzato dalla piattaforma Java e ciò permette di distribuire le app in piattaforme differenti. Java può essere utilizzato nei sistemi embedded, nei dispositivi mobili, nei server enterprise e anche nei supercomputer.

Il linguaggio principale usato per generare codice che viene distribuito come byte code in una Java Virtual Machine (JVM) è Java. Nonostante ciò, altri compilatori permettono la generazione di byte code a partire da diversi linguaggi di programmazione, come JavaScript, Python o Ruby. Ci sono inoltre linguaggi che possono essere eseguiti in modo nativo nella JVM, come Scala o Apache Groovy. Per quanto riguarda la sintassi, Java ha preso molto in prestito dal C e dal C++, mentre per le caratteristiche orientate agli oggetti si è ispirata a Smalltalk e all’Objective-C. Uno dei punti fondamentali di Java è che non prevede puntatori e altri costrutti di così basso livello. Ogni oggetto creato in Java viene allocato sullo heap e tutte le variabili di tipi oggetti sono dei riferimenti. La gestione della memoria è effettuata con un meccanismo di garbage collection, così come visto in .NET.
Nel novembre del 2006, Sun Microsystem ha deciso di rendere disponibile con licensa GNU (General Public License) la sua implementazione di Java.

Principio di funzionamento

Le componenti essenziali della piattaforma Java sono la Java Virtual Machine e le API Java, cioè un insieme di librerie che permettono di svolgere qualsiasi tipo di compito. Le applicazioni software per poter essere compatibili con una piattaforma Java devono essere scritte in uno dei linguaggi supportati dalla macchina virtuale (come per esempio Java, Scala o altri) e quindi compilate in modo da ottenere il byte code che può essere interpretato ed eseguito dalla macchina virtuale. Il byte code non dipende da alcuna macchina o sistema operativo in particolare, ma è il più possibile astratto dal sistema in cui verrà poi eseguita l’applicazione. L’unica cosa che cambia in base alla tipologia della macchina è l’interprete che riceve in ingresso il byte code dell’applicazione.

Il compilatore Java ha il compito di convertire il codice sorgente Java in byte code ed è fornito dal Java Development Kit (JDK). Il Java Runtime Environment (JRE) integra la Java Virtual Machine con un compilatore just-in-time, che converte al volo il byte code in modo da ottenere codice macchina.

Dalla piattaforma Java derivano diverse altre piattaforme che si focalizzano su specifiche tipologie di dispositivi, come la Java Card (per realizzare applicazioni che eseguono su Smart card o altri dispositivi con memoria limitata), Java ME (Micro Edition, per realizzare applicazioni che eseguono su dispositivi con limitate capacità di storage, di display e di potenza), Java SE (Standard Edition, per applicazioni general-purpose sui PC o server) e Java EE (Enterprise Edition), che arricchisce Java SE con altre API per lo sviluppo di applicazioni client-server multi-tier.

Java Virtual Machine

La Java Virtual Machine (JVM) si occupa di eseguire i byte code virtualizzando l’hardware al di sopra del quale viene eseguita l’applicazione java. Le specifiche che definiscono la JVM non contengono dettagli implementativi che potrebbero rappresentare un problema per garantire l’interoperabilità, come per esempio l’algoritmo di garbage collection utilizzato o eventuali ottimizzazioni interne delle istruzioni. Le applicazioni java possono essere eseguite solo utilizzando un’implementazione concreta delle specifiche astratte della Java Virtual Machine.

Per quanto riguarda l’architettura, la JVM è costituita da un heap garbage-collected in cui vengono salvati oggetti e array. Il codice, le costanti e altre classi dati sono salvati in una parte di heap che prende il nome di area dei metodi. Un’implementazione della Java Virtual Machine potrebbe trattare in modo separato l’heap e l’area dei metodi, decidendo per esempio di non effettuare garbage collection su di essa.

Ogni thread della JVM ha il suo stack di chiamate, all’interno del quale vengono salvati i frame. Un frame viene creato quando viene invocato un metodo e non appena il metodo termina il frame viene eliminato. Ad ogni frame è associato uno stack degli operandi ed un array di variabili locali. Il primo è utilizzato per salvare gli operandi dei calcoli e i valori di ritorno di un metodo chiamato, mentre le variabili locali hanno lo stesso scopo dei registri.

La JVM è costituita da tre componenti fondamentali:

  1. Class loader: Serve a caricare le classi dell’applicazione Java e delle API che costituiscono il byte code. In particolare, il class loader effettua tre azioni. La prima prende il nome di loading e consiste nel cercare e importare i dati binari per ogni tipo. Successivamente l’azione di linking verifica la correttezza del tipo importato, quindi alloca la memoria per le variabili di classe inizializzandola con dei valori di default e infine converte i riferimenti simbolici in riferimenti diretti. L’ultima fase prende il nome di inizializzazione e consiste nell’invocare quel codice java responsabile di assegnare alle variabili di classe i loro valori iniziali. È possibile distinguere tra due tipi di class loader: il bootstrap class loader e l’user defined class loader. Il primo, che si occupa di caricare le classi trusted, deve essere presente obbligatoriamente nell’implementazione di ogni JVM.
  2. Class verifier: La strategia di java è evitare che un’applicazione possa provocare il crash della macchina host o interagire in modo inappropriato con altre operazioni eseguite nella macchina host. Inoltre, java protegge metodi e strutture dati relative al codice fidato dall’accesso di codice non fidato eseguito nella JVM. In ultimo luogo java non permette che avvengano quei tipici errori dei programmatori che causano la corruzione dei dati o altri comportamenti inaspettati, come l’utilizzo di un puntatore non inizializzato o l’accesso oltre la fine di un array.
    Il class verifier è uno degli attori principali nella strategia appena mostrata e si occupa di verificare la validità del byte code. In particolare, verifica che i branch puntino sempre a degli indirizzi di memoria validi, che i dati siano sempre inizializzati assieme al fatto che i riferimenti siano type-safe e, in ultimo luogo, che l’accesso ai campi private o package all’interno delle classi sia corretto. Le prime due operazioni vengono eseguite durante il caricamento di una classe mentre la terza è eseguita in modo dinamico, cioè quando i campi di una classe sono acceduti da altre classi.
    Il verifier assicura anche che le istruzioni di branch possano puntare solo ad un’altra istruzione all’interno dello stesso metodo. Inoltre, permette che una data istruzione faccia riferimento solo ad un indirizzo di memoria fisso nello stack. Questo permette al compilatore just-in-time di convertire gli accessi allo stack in accessi ai registri senza causare una penalità in termini di velocità durante l’emulazione su un architettura basata su registri.
  3. Interprete java: L’interprete permette di eseguire ogni byte code java. Interpreti diversi sono necessari per ogni architettura hardware differente. Il problema è che quando il byte code è eseguito dall’interprete le prestazioni sono peggiori rispetto all’esecuzione del codice macchina ottenuto compilando lo stesso byte code. I compilatori just-in-time servono per superare in parte tale svantaggio. Infatti, durante l’esecuzione di un programma un compilatore del genere traduce il byte code in linguaggio macchina. Le parti di programma che vengono tradotte saranno eseguite più velocemente di quelle che invece vengono interpretate. Naturale conseguenza di quanto detto è che conviene utilizzare questa tecnica per quelle parti di programma che vengono eseguite più frequentemente. È possibile anche implementare l’interprete direttamente a livello hardware: in questo modo il byte code è eseguito come codice nativo. Ciò implica lo sviluppo di un processore con un set di istruzioni equivalente al repertorio di istruzioni definite dalle specifiche Sun Microsystem. Il vantaggio di un approccio del genere è che si ottengono le prestazioni migliori. D’altro lato, lo svantaggio è che il byte code può essere eseguito solo in quelle macchine che implementano java a livello hardware.
API Java

Nei sistemi operativi moderni, gli sviluppatori possono sfruttare la presenza di una grossa quantità di codice riusabile per semplificare il proprio lavoro. Questo codice è rappresentato da un insieme di dynamically linked library (dll) che possono essere caricate dalle applicazione a runtime. Il problema nel caso della piattaforma java è che essa non è associata ad uno specifico sistema operativo e quindi le applicazioni non possono utilizzare eventuali librerie fornite dall’OS. Per risolvere questo problema, la piattaforma java stessa porta con sé un insieme di librerie di classi che offrono funzionalità simili alle librerie messe a disposizione da un sistema operativo. Un esempio è dato dalla libreria Swing, responsabile di disegnare le interfacce utente e gestire gli eventi ad esse associati, nascondendo al programmatore le differenze su come diversi sistemi operativi gestiscono i propri componenti.

Le librerie di classi java hanno tre scopi principali. Il primo è lo stesso di tutte le altre librerie standard, ovvero fornire al programmatore un insieme di funzioni per eseguire le azioni più comuni, come mantenere una lista di oggetti o effettuare complesse operazioni sulle stringhe. In secondo luogo, forniscono un’interfaccia astratta per quelle operazioni che altrimenti dipenderebbero troppo dall’hardware sottostante e da un sistema operativo specifico. Compiti come l’accesso alla rete o ai file sono spesso troppo correlati alle differenti implementazioni di ogni piattaforma. Le librerie java.net e java.io implementano un livello di astrazione nel codice nativo del sistema operativo e forniscono un’interfaccia standard sulla quale le applicazioni java possono fare affidamento per compiere task che riguardano l’uso della rete e dei file. Il terzo e ultimo scopo riguarda il modo in cui viene gestita l’eventuale assenza di una o più caratteristiche che un’applicazione java si aspetta di trovare, ma che non è offerta dalla piattaforma sottostante. In questo caso le librerie di classi possono emulare un componente che fornisca la specifica non presente nell’OS oppure possono fornire un modo consistente per verificare la presenza o meno della caratteristica richiesta.

Le librerie di classi offerte sono così ampie che sono disponibili tre configurazioni diverse della piattaforma java in base all’uso che se ne vuole fare:

  1. Standard edition: Questa configurazione fornisce le API per le esigenze più comuni, permettendo di sviluppare applicazioni stand-alone, applicazioni client-server, applicazioni per accesso a database o altri tipi di applicazioni.
  2. Enterprise edition: Questa configurazione permette di scrivere applicazioni distribuite.
  3. Micro edition: Questa configurazione permette di sviluppare applicazioni per i device con limitate risorse computazionali.
Java EE

La piattaforma Java EE (Enterprise Edition) è una piattaforma derivata da Java ed è molto utilizzata nella programmazione web. L’insieme di specifiche fornite dalla piattaforma Java EE può essere implementato totalmente (in questo caso si usa il termine Full Platform) oppure può essere limitato al Web Profile. In quest’ultimo caso viene implementato solo quel sottoinsieme di specifiche che serve a realizzare applicazioni web.

Originariamente i software che implementavano questo insieme di specifiche venivano chiamati application server, invece oggi sono chiamati Referencing Runtime Java EE. Questo cambiamento è causato dalla nascita dell’architettura a microservizi, in cui viene eseguita un’applicazione java autonoma, senza la necessità di avere un application server. Quindi, mentre in un primo momento la specifica incitava gli sviluppatori a creare applicazioni basate su architettura multi-tier, oggi è possibile sviluppare anche microservizi.
L’insieme di specifiche che costituisce la Java EE estende le funzionalità di base della piattaforma java offrendo una varietà di tecnologie. L’utilizzo di un referencing runtime, comunque, è richiesto solo nelle componenti enterprise (che necessita di transazioni distribuite e di code distribuite). Le altre componenti, come quelle per il web o per le basi dati, possono essere utilizzate anche all’interno di un normale web server. Le componenti principali sono raggruppate nelle seguenti specifiche:

  • Specifiche per i web service
    Java EE permette lo sviluppo di web service di tipo REST o di tipo SOAP. Le specifiche sono:
  • JSON Processing: serve ad elaborare le informazioni in formato JSON.
  • JSON Binding: serve per trasformare gli oggetti java in stringhe JSON e viceversa.
  • RESTful Web Services: serve per implementare web service utilizzando
    l’architettura REST.
  • JAX-WS: serve per realizzare web service di tipo SOAP
  • Java Architecture for XML Binding: serve per trasformare oggetti java in stringhe XML e viceversa.
  • Specifiche web
    Con specifiche web si intendono quelle componenti che servono a visualizzare le pagine web. Le componenti principali sono:

    • Servlet: questa tecnologia serve a trasferire le pagine HTML al client e a esporre i web service.
    • Java Server Faces: racchiude un insieme di componenti tramite i quali si possono sviluppare siti con funzionalità AJAX
  • Specifiche enterprise
    Includono quelle componenti che sono caratteristiche proprie di Java EE.

    • Contexts and Dependency Injection: permettono di usare l’inversione di controllo.
    • Enterprise JavaBeans: offrono un sistema con le specifiche richieste dalle applicazioni enterprise, quali scalabilità, sicurezza, persistenza dei dati e altro. Queste caratteristiche sono oggi disponibili anche con Contexts and Dependency Injection.
    • Java Message Service: offre un sistema per inviare e gestire messaggi.
    • Java EE Security API: serve per gestire gli utenti, le password, i gruppi e le autenticazioni.
  • Specifiche per l’interazione con la base dati
    Le componenti che ricadono in questa categoria servono a gestire la persistenza dei datiall’interno dei database relazionali.

    • Java Transaction API: serve a gestire le transazioni distribuite.
    • Java DataBase Connectivity: offre un’interfaccia per accedere a qualsiasi tipo di database.
    • Java Persistence API: contiene le classi che vengono utilizzate per gestire la persistenza dei dati.

.NET vs Java

Dopo aver introdotto esaustivamente le due piattaforme principali, quella .NET e quella Java, è arrivato il momento di confrontarle per capire quali sono le similitudini e le differenze tra le due.

Similitudini tra .NET e Java

Dal punto di vista architetturale, il Common Language Runtime, il Common Intermediate Language e il linguaggio C# sono simili rispettivamente alla Java Virtual Machine, al byte code e al linguaggio Java.
Naturalmente sia .NET che Java offrono un insieme di componenti e servizi standard che semplificano lo sviluppo delle applicazioni. Questo permette agli sviluppatori di concentrarsi sulla business logic anziché perdere tempo a implementare servizi già pronti. Infatti, sia Java che .NET forniscono dei modi standard per eseguire operazioni quali l’accesso al database, la connessione a risorse remote e lo scripting delle pagine web.
Entrambe le piattaforme permettono agli sviluppatori di realizzare applicazioni con architettura multi-tier. Inoltre, entrambe sono orientate agli oggetti, offrono un sistema di tipi sicuro e includono funzionalità di garbage collection automatiche.

Differenze tra .NET e Java

Mentre .NET ha come target principale i sistemi operativi Windows (ad eccezione di Mono), Java si basa sull’idea che lo stesso software possa essere eseguito su tipi di computer differenti, senza dover essere riscritto. Questo perché le applicazioni java sono eseguite nel contesto delle Java Virtual Machine, che sono implementate per la maggior parte delle piattaforme.

Un’altra differenza è che nonostante il framework .NET sia un prodotto libero, esso è stato sviluppato da Microsoft ed integrato nei sistemi operativi Windows. Quindi non fa affidamento su alcun fornitore di terze parti nell’implementazione della piattaforma. Tuttavia, questo framework è altamente estensibile e nel corso degli anni sono state sviluppate molte estensioni ad opera di terze parti. D’altro lato, la piattaforma Java è open source, anche se ci sono diversi venditori che ne forniscono una propria implementazione. La competizione che si instaura tra i fornitori di Java è una delle forze principali di Java stessa, perché promuove l’innovazione, mentre gli standard di compatibilità garantiscono la qualità dei prodotti.

Per la piattaforma .NET Microsoft ha sviluppato un IDE standard, Microsoft Visual Studio. Esso offre tutto ciò che serve ad uno sviluppatore per quanto riguarda lo sviluppo, il debug e le operazioni di build e deploy. Dall’altro lato, non esiste un IDE standard per la piattaforma

Java e gli sviluppatori sono liberi di scegliere quello che più soddisfa i propri bisogni. Gli IDE principali in ambito Java sono Eclipse, IntelliJ Idea, Oracle NetBeans e Oracle JDeveloper.

Come scegliere tra .NET e Java

Senza dubbio entrambe le piattaforme (.NET e Java) permettono di sviluppare applicazioni enterprise di elevata qualità. Tuttavia, la scelta di una tecnologia non ricade solo su quanto viene offerto in termini di performance.
La piattaforma .NET sembra essere avvantaggiata nello sviluppo di applicazioni basate su interfacce utente molto ricche. D’altro lato, Java rappresenta una soluzione migliore per le applicazioni che devono supportare un elevato numero di utenti. In secondo luogo, se l’applicazione sviluppata deve essere portabile su sistemi diversi conviene puntare su Java, che è multipiattaforma per natura.
Nel caso in cui la scelta sia a livello aziendale, bisogna considerare qual è la tecnologia che è già predominante all’interno dell’azienda, poiché rimpiazzare le infrastrutture potrebbe essere costoso. Allo stesso modo bisogna tenere in conto le skill degli sviluppatori a disposizione, perché si potrebbero affrontare costi elevati per la formazione dei developer nel caso in cui si voglia cambiare tecnologia.

Caratteristiche, similitudini e differenze tra .NET e Java per lo sviluppo software
.NET VS Java

Pubblicato da Vito Lavecchia

Lavecchia Vito Ingegnere Informatico (Politecnico di Bari) Email: [email protected] Sito Web: https://vitolavecchia.altervista.org

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *