0% ont trouvé ce document utile (0 vote)
2 vues25 pages

Concepts avancés en programmation Java

Transféré par

Bilios Laporte
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats DOCX, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
2 vues25 pages

Concepts avancés en programmation Java

Transféré par

Bilios Laporte
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats DOCX, PDF, TXT ou lisez en ligne sur Scribd

SOMMAIRE

INTRODUCTION
A. LES ENTREES-SORTIES, LES THREADS, LES SOCKETS
1. LES ENTREES-SORTIES
2. LES THREADS
3. LES SOCKETS
B. LES ANNOTATIONS DE TYPE, LA REFLEXION, LES AGENTS
1. LES ANNOTATIONS DE TYPE
2. LA REFLEXION
3. LES AGENTS
C. LES FRAMEWORKS ET BIBLIOTHEQUES POPULAIRES EN JAVA :
SPRING, HIBERNATE, JUNIT
1. SPRING
2. HIBERNATE
3. JUNIT
CONCLUSION
INTRODUCTION

Le Java est un langage de programmation très populaire utilisé par les développeurs pour
créer des applications web et desktop. Il est connu pour sa portabilité, sa sécurité, sa performance
et sa facilité d'utilisation. Java avancée est une étape supérieure pour les développeurs qui
cherchent à maîtriser davantage les fonctionnalités et les technologies les plus complexes du
développement Java.

Java avancée fait référence à l'utilisation de concepts et de fonctionnalités plus avancés


dans le développement d'applications Java. Cela peut inclure l'utilisation de la réflexion, des
annotations, des lambdas, des flux, des threads, des expressions régulières, des génériques, de la
sérialisation, des API réseau, et bien plus encore.

En utilisant ces fonctionnalités avancées, les développeurs Java peuvent créer des
applications plus complexes et plus efficaces, capables de répondre à des exigences plus
importantes. Par exemple, la synchronisation des threads peut être utilisée pour garantir que les
tâches sont exécutées en parallèle de manière efficace, la sérialisation peut être utilisée pour
transférer des objets entre différents environnements Java, et la réflexion peut être utilisée pour
insérer et modifier dynamiquement des éléments dans une application Java pendant son
exécution.

Java avancée peut être utilisée pour développer des solutions dans différents domaines,
tels que les applications web, les applications mobiles, les systèmes distribués, la finance, la
sécurité, le machine learning, l'analyse de données et bien plus encore. En somme, Java avancée
est un ensemble de fonctionnalités et de concepts qui permettent aux développeurs Java de mettre
en place des solutions plus puissantes et plus sophistiquées pour répondre aux besoins exigeants
de l'industrie.
A. LES ENTREES-SORTIES, LES THREADS, LES SOCKETS

1- LES ENTREES-SORTIES
Les entrées-sorties en Java avancé sont gérées par les classes du package [Link]
([Link], [Link], [Link] et [Link]). Ces
classes obéissent au patron de conception décorateur. Les entrées/sorties (E/S) en Java permettent
à un programme Java d'interagir avec le monde extérieur en lisant des données à partir de sources
externes ou en écrivant des données dans des destinations externes.

 InputStream et classes concrètes


La classe InputStream est une classe abstraite qui représente un flux d’entrée de données
binaires. Elle déclare des méthodes read qui permettent de lire des données octet par octet ou bien
de les copier dans un tableau. Ces méthodes retournent le nombre de caractères lus ou -1 pour
signaler la fin du flux. Il existe plusieurs classes qui en fournissent une implémentation concrète.

- La classe ByteArrayInputStream permet d’ouvrir un flux de lecture binaire sur un


tableau de byte.
- La classe FileInputStream permet d’ouvrir un flux de lecture binaire sur un fichier.
 OutputStream et classes concrètes
La classe OutputStream est une classe abstraite qui représente un flux de sortie de
données binaires. Elle déclare des méthodes write qui permettent d’écrire des données octet par
octet ou bien de les écrire depuis un tableau. La classe OutputStream fournit également la
méthode flush pour forcer l’écriture de la zone tampon (s’il existe une zone tampon sinon un
appel à cette méthode est sans effet).

Il existe plusieurs classes qui en fournissent une implémentation concrète.

- La classe ByteArrayOutputStream permet d’ouvrir un flux d’écriture binaire en


mémoire.
- La classe FileOutputStream permet d’ouvrir un flux d’écriture binaire sur un fichier.
 Reader et classes concrètes
La classe Reader est une classe abstraite qui permet de lire des flux de caractères.
Comme InputStream, la classe Reader fournit des méthodes read mais qui acceptent en paramètre
des caractères. Il existe plusieurs classes qui en fournissent une implémentation concrète.

- La classe StringReader permet de parcourir une chaîne de caractères sous la forme d’un
flux de caractères.
- La classe FileReader permet de lire le contenu d’un fichier texte.

 Writer et classes concrètes


La classe Writer est une classe abstraite qui permet d’écrire des flux de caractères.
Comme OutputStream, la classe Writer fournit des méthodes write mais qui acceptent en
paramètre des caractères. Elle fournit également des méthodes append qui réalisent le même type
d’opérations et qui retournent l’instance du Writer afin de pouvoir chaîner les appels. Il existe
plusieurs classes qui en fournissent une implémentation concrète.

- La classe StringWriter permet d’écrire dans un flux caractères pour ensuite produire une
chaîne de caractères.
- La classe FileWriter permet d’écrire un flux de caractères dans un fichier.

 Flux orientés caractères


Le package [Link] contient un ensemble de classes qui permettent de manipuler des flux
caractères et donc du texte. Toutes les classes qui permettent d’écrire dans un flux de caractères
héritent de la classe abstraite Writer et toutes les classes qui permettent de lire un flux de
caractères héritent de la classe abstraite Reader.

 Les décorateurs de flux


Le package [Link] fournit un ensemble de classes qui agissent comme des décorateurs pour des
instances de type InputStream, Reader, OutputStream ou Writer. Ces décorateurs permettent
d’ajouter des fonctionnalités tout en présentant les mêmes méthodes. Il est donc très simple
d’utiliser ces décorateurs dans du code initialement implémenté pour manipuler des instances des
types décorés.

- Les classes BufferedInputStream, BufferedReader, BufferedOutputStream et


BufferedWriter permettent de créer un décorateur qui gère une zone tampon dont il est
possible d’indiquer la taille à la construction de l’objet. Ces classes sont très utiles
lorsque l’on veut lire ou écrire des données sur un disque ou sur un réseau afin de limiter
les accès système et améliorer les performances.
- La classe LineNumberReader permet quant à elle, de compter les lignes lors de la
lecture d’un flux caractères. Elle fournit également la méthode readLine pour lire une
ligne complète.
- Les classes InputStreamReader et OutputStreamWriter permettent de manipuler un flux
binaire sous la forme d’un flux caractères. La classe InputStreamReader hérite
de Reader et prend comme paramètre de constructeur une instance de InputStream. La
classe OutputStreamWriter hérite de Writer et prend comme paramètre de constructeur
une instance de OutputStream. Ces classes sont particulièrement utiles car elles
permettent de préciser l’encodage des caractères (charset) qui doit être utilisé pour passer
d’un flux binaire au flux caractères et vice-versa.

Les objets statiques [Link], [Link] et [Link] qui représentent respectivement


le flux d’entrée de la console, le flux de sortie de la console et le flux de sortie d’erreur de la
console sont des instances de InputStream ou de PrintStream. PrintStream est un décorateur qui
offre notamment les méthodes print, println et printf.

 La classe Scanner
La classe [Link] agit comme un décorateur pour différents types d’instance qui
représentent une entrée. Elle permet de réaliser des opérations de lecture et de validation de
données plus complexes que les classes du packages [Link].

 Fichiers et chemins
En plus des flux de type fichier, le package [Link] fournit la classe File qui représente un
fichier. À travers, cette classe, il est possible de savoir si le fichier existe, s’il s’agit d’un
répertoire… On peut également créer le fichier ou le supprimer.

 Accès au réseau
La classe URL, comme son nom l’indique, représente une URL. Elle déclare la
méthode openConnection qui retourne une instance de URLConnection. Une instance
de URLConnection ouvre une connexion distante avec le serveur et permet de récupérer des
informations du serveur distant. Elle permet surtout d’obtenir une instance de OutputStream si on
désire envoyer des informations au serveur et une instance de InputStream si on désire récupérer
les informations retournées par le serveur.
2- LES THREADS
Les ``threads'' ou ``processus légers'' sont des unités d'exécution autonomes qui peuvent
effectuer des tâches, en parallèle avec d'autres threads: ils sont constitués d'un identificateur, d'un
compteur de programme, d'une pile et d'un ensemble de variables locales. Le flot de contrôle d'un
thread est donc purement séquentiel. Plusieurs threads peuvent être associés à un ``processus
lourd'' (qui possède donc un flot de contrôle multiple, ou parallèle). Tous les threads associés à un
processus lourd ont en commun un certain nombre de ressources, telles que: une partie du code à
exécuter, une partie des données, des fichiers ouverts et des signaux.

En Java, le processus lourd sera la JVM (Java Virtual Machine) qui interprète le bytecode
des différents processus légers. Les threads coopèrent entre eux en échangeant des valeurs par la
mémoire commune (du processus lourd). L'intérêt d'un système ``multi-threadé'', même sur une
machine monoprocesseur, est que l'ordinateur donne l'impression d'effectuer plusieurs tâches en
parallèle. Les systèmes d'exploitation modernes (Linux, Windows XP, MacOS X etc.) sont tous
multi-threadés contrairement aux premiers OS de micro-ordinateurs. Le fait de pouvoir ouvrir en
même temps netscape, emacs, et un shell par exemple, et de pouvoir passer d'une fenêtre à l'autre
sans attente est la marque d'un tel système. L'application netscape même est multi-threadée. Une
tâche essaie de se connecter au site choisi, pendant qu'une autre imprime à l'écran etc. Imaginez
ce que ce serait si vous ne voyiez rien à l'écran tant que le site auquel vous vous connectez n'a pas
fini de vous transmettre toutes les données! En fait, un système d'exploitation comme Unix
comporte des processus (lourds) multiples, tels les démons systèmes (ou processus noyau) et
souvent un grand nombre de processus (lourds) utilisateurs. Il n'est pas rare d'avoir quelques
dizaines voire une centaine de processus lourds sur une machine à tout instant (faire ps -al par
exemple).
Sur une machine multiprocesseur, des threads peuvent être exécutés sur plusieurs processeurs
donc réellement en même temps, quand le système d'exploitation et le support pour les threads
sont étudiés pour (c'est le cas pour Windows NT, Solaris 2, Linux etc.). Pour rentrer un peu plus
dans les détails, les threads que nous programmerons sont des ``threads utilisateurs'' qui doivent
communiquer avec le noyau de système d'exploitation de temps en temps, ne serait-ce que pour
imprimer à l'écran, lire et écrire des fichiers etc. Tout thread utilisateur doit donc être lié d'une
façon ou d'une autre à un thread ``noyau''. Selon les implémentations, chaque tâche utilisateur
peut être liée à une tâche noyau, ou plusieurs tâches utilisateur à plusieurs tâches noyaux, ou
encore plusieurs tâches utilisateur à une tâche noyau. La première version de Solaris (``green
threads'') implémentait seulement la dernière possibilité qui est la seule qui ne permet pas de
bénéficier de vrai parallélisme sur une architecture multiprocesseur. A partir de Java 1.1 et pour
les versions plus récentes de Solaris et de Linux, on est dans le deuxième cas, qui offre le plus de
flexibilité et de performances.

Les threads (ou processus légers) sont définis dans le langage JAVA, et ne sont pas
comme en C ou C++, une extension que l'on peut trouver dans différentes bibliothèques.

 Création

Pour créer un thread, on crée une instance de la classe Thread,

Thread Proc = new Thread ();

Une fois créé, on peut configurer Proc, par exemple lui associer une priorité . On pourra
ensuite l'exécuter en invoquant sa méthode start. Cette méthode va à son tour invoquer la
méthode run du thread. Comme la méthode run de la classe Thread ne fait rien, il faut la
surcharger. C'est possible par exemple si on définit Proc comme une instance d'une sous-classe
de Thread, dans laquelle on redéfinit la méthode run.

En général on a envie qu'un processus contienne des données locales, donc il est vraiment
naturel de définir un processus comme une instance d'une sous-classe de Thread en général,

Classe Compte programmée par extension de la classe Thread.

class Compte extends Thread {


int valeur;
Compte(int val) {
valeur = val;
}
public void run() {
try {
for (;;) {
[Link](valeur + " ");
sleep(100);
}
} catch (InterruptedException e) {
return;
}
}
public static void main(String[] args) {
new Compte(1).start();
new Compte(2000).start();
}
}
La classe Compte gère uniquement ici un entier représentant une somme d'un compte
courant d'une banque. Après avoir défini le constructeur, on a écrit une méthode run qui sera
exécutée deux fois par deux instances différentes de la classe à partir de la main, l'une avec une
valeur de compte initiale égale à 1 et l'autre, à 2000. La méthode run se contente d'afficher la
valeur courante du compte tous les dixièmes de seconde, jusqu'à une interruption clavier.

L'exécution du programme main donne quelque chose comme,

> java Compte


1 2000 1 2000 1 1 2000 1
^C
>
 Quelques fonctions élémentaires sur les threads
Pour s'amuser (disons que c'est au moins utile pour débugger) on peut nommer les
threads: void setName(String name) nomme le thread courant. String getName() renvoie le nom
du thread.

Un peu plus utile maintenant: on peut recueillir un certain nombre d'informations sur les
threads présents à l'exécution. Par exemple, static Thread currentThread() renvoie la référence au
thread courant c'est-à-dire celui qui exécute currentThread(). La méthode int enumerate(Thread[]
threadArray) place tous les threads existants (y compris le main() mais pas le thread ramasse
miettes) dans le tableau threadArray et renvoie leur nombre. static int activeCount() renvoie le
nombre de threads actifs (on définira mieux ce que cela peut être aux sections suivantes).
Voici un petit exemple d'utilisation

Nouvelle implémentation de la classe Compte.

class Compte3 extends Thread {


int valeur;

Compte3(int val) {
valeur = val;
}

public void run() {


try {
for (;;) {
[Link](valeur + " ");
sleep(100);
}
} catch (InterruptedException e) {
return; } }

public static void printThreads() {


Thread[] ta = new Thread[[Link]()];
int n = [Link](ta);
for (int i=0; i<n; i++) {
[Link]("Le thread "+ i + " est
" + ta[i].getName());
}}

public static void main(String[] args) {


new Compte3(1).start();
new Compte3(2000).start();
printThreads(); } }

Son exécution donne:

% java Compte3
1 2000 Le thread 0 est main
Le thread 1 est Thread-2
Le thread 2 est Thread-3
1 2000 1 2000 1 2000 1 2000 2000
1 1 2000 2000 1 1 2000 2000 1 1
2000 2000 1 1 2000 2000 1 1 2000
2000 1 1 2000 2000 1 1 ^C
%

3- LES SOCKETS
Le terme programmation de socket fait référence à l'écriture de programmes qui
s'exécutent sur plusieurs ordinateurs dans lesquels les périphériques sont tous connectés les uns
aux autres à l'aide d'un réseau.
Il existe deux protocoles de communication que nous pouvons utiliser pour la
programmation des sockets : User Datagram Protocol (UDP) et Transfer Control Protocol
(TCP) .
La principale différence entre les deux est que UDP est sans connexion, ce qui signifie
qu'il n'y a pas de session entre le client et le serveur, tandis que TCP est orienté connexion, ce qui
signifie qu'une connexion exclusive doit d'abord être établie entre le client et le serveur pour que
la communication ait lieu. .
Ce didacticiel présente une introduction à la programmation de sockets sur des
réseaux TCP/IP et montre comment écrire des applications client/serveur en Java. UDP n'est pas
un protocole courant et, en tant que tel, peut ne pas être rencontré souvent.
 Configuration du projet
Java fournit une collection de classes et d'interfaces qui prennent en charge les détails de
communication de bas niveau entre le client et le serveur.
Ceux-ci sont principalement contenus dans le package [Link] , nous devons donc
effectuer l'importation suivante :
import [Link].*;Copie
Nous avons également besoin du package [Link] , qui nous donne des flux d'entrée et
de sortie pour écrire et lire tout en communiquant :
import [Link].*;Copie
Par souci de simplicité, nous exécuterons nos programmes client et serveur sur le même
ordinateur. Si nous devions les exécuter sur différents ordinateurs en réseau, la seule chose qui
changerait serait l'adresse IP. Dans ce cas, nous utiliserons localhost sur [Link] .
 Exemple simple
Mettons-nous la main à la pâte avec les exemples les plus élémentaires impliquant un
client et un serveur . Ce sera une application de communication bidirectionnelle où le client
salue le serveur et le serveur répond.
Nous allons créer l'application serveur dans une classe appelée [Link] avec le
code suivant.
Nous inclurons la méthode principale et les variables globales pour attirer l'attention sur
la façon dont nous allons exécuter tous les serveurs dans cet article. Pour le reste des exemples de
cet article, nous omettons ce type de code répétitif :

public class GreetServer {


private ServerSocket serverSocket;
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;

public void start(int port) {


serverSocket = new ServerSocket(port);
clientSocket = [Link]();
out = new PrintWriter([Link](), true);
in = new BufferedReader(new InputStreamReader([Link]()));
String greeting = [Link]();
if ("hello server".equals(greeting)) {
[Link]("hello client");
}
else {
[Link]("unrecognised greeting");
}
}
public void stop() {
[Link]();
[Link]();
[Link]();
[Link]();
}
public static void main(String[] args) {
GreetServer server=new GreetServer();
[Link](6666);
}
}
Nous allons également créer un client appelé [Link] avec ce code :
public class GreetClient {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;

public void startConnection(String ip, int port) {


clientSocket = new Socket(ip, port);
out = new PrintWriter([Link](), true);
in = new BufferedReader(new InputStreamReader([Link]()));
}
public String sendMessage(String msg) {
[Link](msg);
String resp = [Link]();
return resp;
}
public void stopConnection() {
[Link]();
[Link]();
[Link]();
}
}
Maintenant, démarrons le serveur. Dans notre IDE, nous le faisons simplement en
l'exécutant comme une application Java.
Ensuite, nous enverrons un message d'accueil au serveur à l'aide d'un test unitaire, qui
confirme que le serveur envoie un message d'accueil en réponse :
@Test
public void givenGreetingClient_whenServerRespondsWhenStarted_thenCorrect() {
GreetClient client = new GreetClient();
[Link]("[Link]", 6666);
String response = [Link]("hello server");
assertEquals("hello client", response);
}
Cet exemple nous donne une idée de ce à quoi s'attendre plus tard dans l'article. En tant
que tel, nous ne comprenons peut-être pas encore complètement ce qui se passe ici.
Dans les sections suivantes, nous allons disséquer la communication par socket à
l'aide de cet exemple simple, et plonger également dans des exemples plus complexes.

B. LES ANNOTATIONS DE TYPE, LA REFLEXION, LES


AGENTS
1- LES ANNOTATIONS DE TYPE
Les annotations de type en Java sont des mécanismes qui permettent d’ajouter des
informations supplémentaires à du code source Java. Une annotation être appliqué à une classe,
une méthode, un champ ou à un autre élément de code pour fournir des informations
supplémentaires à propos de cet élément. Les annotations sont généralement utilisées pour
indiquer les contraintes, les paramètres, les auteurs, les versions, etc. des éléments de code.

Les annotations sont utilisées dans des domaines divers. Leur intérêt principal est de
fournir une méta-information qui pourra être exploitée par un programme.

Les annotations sont définies en Java à l'aide du mot-clé "@". Par exemple, @Deprecated
est une annotation qui indique que l'élément de code auquel elle est appliquée est obsolète et qu'il
ne devrait pas être utilisé dans un code de production.

Les annotations en Java peuvent être définies soit par le programmeur, soit par des
frameworks tiers. Les annotations sont également utilisées dans le cadre de certaines API Java
standard, telles que JPA (Java Persistence API) et JAX-RS (Java API for RESTful Web Services).

Les annotations en Java peuvent être utilisées à la fois pour la documentation du code et
pour la génération automatique de code. Les outils de génération de code, tels que les IDE,
peuvent utiliser les annotations pour générer du code supplémentaire ou pour augmenter la
productivité du développeur

 Utilisation des annotations

Une annotation est un type (comme une classe ou une interface) du langage Java : elle
peut être référencée par son nom complet ou importée depuis un autre paquet grâce au mot-
clé import.

Une annotation n’est pas instanciée, elle est accolée à l’élément qu’elle vient enrichir :
package [Link];

public class Voiture {

@Override
public String toString() {
return "une voiture";
}
}
L’annotation Override est définie dans le package [Link] (c’est pour cela qu’il n’est pas
nécessaire de l’importer explicitement). Cette annotation est utilisable uniquement sur les
méthodes pour indiquer que la méthode est une redéfinition d’une méthode d’une classe parente
(dans l’exemple précédent, la méthode redéfinit [Link]). Cette annotation est exploitée
par le compilateur pour réaliser des vérifications supplémentaires.

Exemple d’annotation :
- Deprecated

Permet de générer des warnings afin d’informer les autres développeurs que quelque chose (une
classe, une méthode…) a été dépréciée et ne devrait plus être utilisée.
- FunctionalInterface

Permet au compilateur de s’assurer que l’interface qui porte cette annotation peut être
implémentée par un lambda (Cf. le chapitre sur les lambdas).
- Override

Signale qu’une méthode est une redéfinition d’une méthode déclarée dans une classe parente.
Cela permet au compilateur de signaler une erreur si ce n’est pas le cas.
- SuppressWarnings

Permet de forcer le compilateur à ne plus émettre d’avertissement à la compilation dans certains


cas.
- SafeVarargs

Cette annotation s’ajoute à une méthode acceptant un paramètre variable (varargs) dont le
type est un générique. En effet, le principe de l’effacement de type (type erasure) dans la gestion
des classes génériques fait qu’il est possible de corrompre un type paramétré utilisé comme
paramètre variable sans que le compilateur et la JVM ne puissent le détecter. Pour pallier à ce
problème, le compilateur produit systématiquement un avertissement lorsqu’on utilise un type
générique comme paramètre variable. Cette annotation permet de supprimer l’avertissement à la
compilation et implique que le développeur s’est assuré que son implémentation est sûre.

L’API standard de Java (mais également des bibliothèques tierces) fournissent beaucoup
d’autres annotations qui ne sont pas interprétées par le compilateur mais par le programme lui-
même à l’exécution.
 Déclaration d’une annotation
Comme pour les classes, les interfaces et les énumérations, on crée une annotation dans
un fichier portant le même nom que l’annotation avec l’extension .java. On déclare une
annotation avec le mot-clé @interface.
package [Link];

public @interface MyAnnotation {

}
La déclaration des attributs d’une annotation est la suivante :
package [Link];

public @interface MyAnnotation {


String name();
boolean isOk();
int[] range() default {1, 2, 3};
}

Les attributs d’une annotation peuvent être uniquement :

 un type primitif,
 une chaîne de caractères ([Link]),
 une référence de classe ([Link]),
 une Annotation ([Link]),
 une énumération,
 un tableau à une dimension d’un de ces types.
La déclaration d’une annotation peut elle-même être annotée par :
- Documented

Pour indiquer si l’annotation doit apparaître dans la documentation générée par un outil
comme javadoc.

- Inherited

Pour indiquer que l’annotation doit être héritée par la classe fille.

- Retention

Pour préciser le niveau de rétention de l’annotation (Cf. ci-dessous).


- Target

Pour indiquer quels types d’éléments peuvent utiliser l’annotation : classe, méthode, attribut…
- Repeatable
Pour indiquer qu’une annotation peut être déclarée plusieurs fois sur un même élément.

2- LA REFLEXION
La réflexion en Java désigne la capacité d'un programme à examiner et manipuler à
runtime (au moment de l'exécution) les classes, les méthodes, les attributs, etc. d'un programme.
Elle permet d'obtenir des informations sur une classe (nom, champs, méthodes, annotations, etc.),
de créer des instances de classe dynamiquement, d'appeler des méthodes en utilisant des noms ou
des chaînes de caractères, ou encore de modifier des membres privés d'une classe.

Pour utiliser la réflexion en Java, il faut utiliser la classe Class, qui permet de représenter
une classe en Java. Cette classe propose des méthodes pour accéder aux informations relatives à
une classe (getDeclaredFields(), getDeclaredMethods(), getAnnotations(), etc.). On peut
également utiliser la classe Method pour exécuter une méthode dynamiquement, ou encore la
classe Constructor pour créer une instance de classe.

Cependant, la réflexion en Java peut être coûteuse en termes de performances et peut


rendre le code plus compliqué à lire et maintenir. Il est donc recommandé de limiter son
utilisation aux cas où elle est vraiment nécessaire, par exemple pour créer des plugins ou des
frameworks génériques.

 Les avantages de la réflexion en Java sont les suivants :

- Elle permet d'inspecter les classes à l'exécution.

- Elle permet de charger des classes à l'exécution.

- Elle permet d'appeler des méthodes à l'exécution.

- Elle permet de créer des instances de classes à l'exécution.

- Elle permet de récupérer et de modifier les champs d'une classe à l'exécution.

- Elle permet de créer des tableaux à l'exécution.

 Comment utiliser la réflexion pour créer des objets


Pour créer une instance de classe à l'exécution en utilisant la réflexion, vous pouvez
utiliser la méthode `newInstance()` de la classe `[Link]`. Cette méthode crée une
nouvelle instance de la classe en appelant le constructeur sans paramètre de la classe (qui doit
donc en posséder un). Par exemple :

Class<?> c = [Link]("[Link]");

Object obj = [Link]();

Si la classe n'a pas de constructeur sans paramètres, on peut d'abord obtenir le constructeur, puis
l'appeler. Par exemple :

Class<?> c = [Link]("[Link]");

Constructor<?> constructor = [Link]([Link]);

Object obj = [Link]("param1");

Les exceptions doivent être gérées correctement.

3- LES AGENTS
Les agents en Java sont des programmes autonomes qui utilisent la plate-forme Java pour
effectuer diverses tâches. Ils sont souvent utilisés dans les systèmes distribués pour faciliter la
communication et la coordination entre les différents nœuds du réseau.

Les agents peuvent être implémentés à l'aide de plusieurs bibliothèques et frameworks,


tels que JADE, JaCaMo, ou encore Apache River. Ces outils proposent des fonctionnalités
avancées pour la gestion des agents, la planification des tâches, la communication inter-agents et
avec le système hôte.

Les agents Java sont souvent utilisés dans les applications d'intelligence artificielle, de
gestion de connaissances, de surveillance et de sécurité, de contrôle de processus industriels, et
d'optimisation de chaînes logistiques. Les agents peuvent être exécutés sur des ordinateurs locaux
ou distants, et peuvent être programmés pour interagir avec des robots, des capteurs, des bases de
données, des services web, ou tout autre système connecté au réseau.
Les avantages des agents Java incluent leur modularité, leur adaptabilité, leur résilience,
leur interopérabilité, et leur capacité à traiter de grandes quantités de données en temps réel.
Cependant, ils peuvent être plus complexes à développer et à maintenir que d'autres types de
programmes, et nécessitent souvent des compétences spécialisées en intelligence artificielle et en
ingénierie logicielle

 Utilisations des agents Java


Il peut y avoir de nombreuses utilisations des agents Java, telles que la programmation
orientée aspect (AOP), les tests de mutation, le profilage, etc. AOP ajoute généralement un
comportement tel que la journalisation ou la sécurité à un programme existant sans modifier le
code. Il utilise des agents Java pour manipuler le bytecode et a un effet combiné de sa
fonctionnalité avec le programme. La surveillance des paramètres de niveau JVM tels que la
création d’objets, le nettoyage de la mémoire, l’exécution de threads, etc., est le travail du
profileur. Les outils de profilage utilisent de manière significative les agents Java pour profiler les
paramètres JVM du programme en cours d’exécution.
Il existe de nombreuses autres situations où les agents Java ainsi que l’API
d’instrumentation sont très utiles.

 Comment écrire des agents Java


La classe qui implémente l’agent Java doit implémenter une méthode appelée
public static void premain(String agentArgs, Instrumentation inst)
Cette méthode constitue le point d’entrée de l’agent, tout comme le point d’entrée d’un
programme Java standard est la méthode principale.
Après l’initialisation de la JVM, la méthode premain est appelée ; Cela représente l’agent.
Il peut y avoir plusieurs agents de ce type; par conséquent, chacune des méthodes premain est
appelée selon l’ordre des agents spécifié lors de l’initialisation de la machine virtuelle de
surveillance. Si une méthode premain spécifique n’est pas trouvée, JVM appelle à son tour la
version surchargée de la méthode premain telle que
public static void premain(String agentArgs)
La classe d’agent peut également contenir une méthode généralement utilisée par la
machine virtuelle Java après le démarrage de l’agent, telle que
public static void agentmain(String agentArgs, instrumentation inst)
Ou, sa version surchargée
public static void agentmain(String agentArgs)
C’est la routine typique de la JVM et une fois cette routine terminée, la méthode
principale est appelée.
Une autre chose importante est que les agents Java doivent inclure un MANIFEST. MF dans
le dossier META-INF du répertoire de ressources pendant le développement. Ce fichier contient
des informations de métadonnées sur la distribution des packages. Ce fichier est inclus dans son
empaquetage JAR. Attributs inclus dans le MANIFEST.

 Classe d’agent
La classe d’agent d’instrumentation avec la méthode premain est utilisée pour récupérer les
informations dont nous avons besoin. L’implémentation de l’interface Instrumentation est passée
à la méthode premain. Nous utilisons la méthode getObjectSize définie par l’interface
d’instrumentation pour obtenir l’utilisation de la mémoire de l’objet Main au moment de
l’exécution.

package [Link];
import [Link];
public class MyAgentClass {
public static void premain(String agentArgs,
Instrumentation inst) {
[Link]([Link]
(new Main()))
}
}
 Déploiement d’agents Java
Une fois qu’un agent est créé, il est déployé en tant que fichier JAR. L’attribut du fichier
manifeste spécifie la classe d’agent qui sera chargée pour démarrer l’agent. Notez qu’il existe de
nombreuses façons de démarrer un agent : en utilisant la ligne de commande, au moment de
l’exécution ou en tant qu’exécutables JAR. Nous allons utiliser la ligne de commande ici.

La puissance fournie par l’API d’instrumentation est ouverte à de nombreuses


innovations. AOP est un exemple simple. Bien que les agents Java et les API Java
Instrumentation ne soient pas très fréquemment utilisés dans le développement d’applications,
l’idée de ce dont il s’agit peut clarifier de nombreux autres aspects de Java. L’exemple de code
donné ici est rudimentaire, juste pour donner une idée de la façon dont les agents sont créés. La
documentation de l’API Java développe de nombreux aspects avec plus de détails ; Consultez-le
pour plus d’informations.

C. LES FRAMEWORKS ET BIBLIOTHEQUES POPULAIRES EN


JAVA : SPRING, HIBERNATE, JUNIT

1- SPRING
Spring est un Framework open-source développé par pivotal et est un Framework
d'application et un conteneur d'inversion de contrôle pour la plate-forme Java qui fournit un
support d'infrastructure pour le développement d'applications

 Utilité de spring

Spring permet aux développeurs de se concentrer sur la logique métier et s'occupe de


l'infrastructure. Spring facilite le développement d'applications Java d'entreprise et prend en
charge Kotlin et Groovy en tant que langages alternatifs sur JVM et offre la flexibilité nécessaire
pour développer des applications avec tout type d'architecture en fonction des besoins. En
général, un développeur écrit une logique métier à l'aide de classes et d'interfaces Java appelées
classes Java anciennes et interfaces Java anciennes. Au printemps, les développeurs peuvent
écrire leur logique dans des classes Java anciennes et fournir des métadonnées dans un fichier
XML et le conteneur Spring crée des objets qui peuvent être utilisés par les développeurs dans le
projet. La dépendance à l'application est fournie par Spring Framework et s'appelle l'injection de
dépendance.

2- HIBERNATE
Hibernate est un Framework Java qui fournit un mappage objet-relationnel vers un
modèle orienté objet vers la base de données relationnelle. Cela signifie qu’Hibernate fournit des
classes Java aux tables de base de données et fournit également une fonction d'interrogation et de
récupération des données.

 Utilité de Hibernate

Le Framework Hibernate fournit une couche d'abstraction qui signifie que les programmeurs
n'ont pas à se soucier de la mise en œuvre. Hibernate implémentera différents modules pour les
développeurs en interne, comme écrire des requêtes pour effectuer des opérations CURD sur la
base de données et établir une connexion à différents types de bases de données. Le Framework
Hibernate est utilisé pour développer une logique de persistance, ce qui signifie stocker et traiter
les données pour une utilisation prolongée. De manière précise, le Framework Hibernate est une
source ouverte pour développer des objets indépendants du logiciel de base de données et créer
une logique de persistance indépendante en Java pour toutes les éditions d'entreprise Java (JEE).

Principales différences entre Spring et Hibernate

Les deux sont des choix populaires sur le marché ; discutons de certaines des différences
majeures :

 Spring est un framework d'application open-source, léger et multiplateforme pour un


développement d'application facile car il prend en charge l'infrastructure et les
développeurs doivent se concentrer sur la logique métier tandis que Hibernate est un
framework entièrement différent pour ORM (mappage objet-relationnel) entre Classes
Java et tables de base de données et fournit des services tels que la récupération de
données et la persistance des données.
 Le framework Spring est utile pour la gestion des transactions, l'injection de
dépendances; la programmation orientée aspect pour les applications, tandis que le
framework Hibernate est utile pour la persistance relationnelle objet, les couches de
données d'accès et les services de récupération de requêtes pour les applications au niveau
de l'entreprise.
 Le framework Spring prend en charge le regroupement de connexions en modifiant le
fichier de configuration, mais il a un problème tel que le pool de connexions peut être
épuisé, tandis que le framework Hibernate prend en charge le regroupement de
connexions en stockant les détails de connexion à la base de données dans un cache et
utilisé ultérieurement, ce qui entraîne une augmentation des performances.
 Le framework Hibernate prend en charge la gestion des versions, c'est-à-dire que
l'utilisateur peut définir les champs de version et les mettre à jour chaque fois qu'il y a un
changement dans l'ensemble de données, tandis que le framework Spring ne prend pas en
charge la gestion des versions, donc les développeurs doivent gérer lors du
développement.
 Spring est un framework d'application open source développé par pivotal qui fournit un
support d'infrastructure aux développeurs et leur permet de se concentrer sur la logique
tandis que Hibernate est un framework open source, léger et multiplateforme développé
par Red Hat.
 Le framework Hibernate offre un mappage objet-relationnel entre les classes Java et les
tables de base de données et n'a pas de modules alors que le framework Spring a
beaucoup de modules dont certains sont Spring core, Spring Security, Spring MVC,
Spring JDBC et bien d'autres modules.

3- JUNIT

La bibliothèque JUnit est un framework de test unitaire pour le langage de programmation


Java. Elle a été créée par Kent Beck et Erich Gamma. JUnit définit deux types de fichiers de tests.
Les TestCase (cas de test) sont des classes contenant un certain nombre de méthodes de tests. Un
TestCase sert généralement à tester le bon fonctionnement d’une classe. Une TestSuite permet
d’exécuter un certain nombre de TestCase déjà définis. Dans un TestCase il n’y a pas de main
méthode, chaque test étant indépendant.

Exemple de TestCase:

Une telle classe hérite de [Link]. La méthode annotée par @Before est
exécutée avant les méthodes de test, celle précédée par @After est appelée à la fin. De la même
manière, la méthode annotée par @BeforeClass est appelée au lancement du testCase, celle
précédée par @AfterClass est appelée juste avant la fin. Les tests sont des méthodes annotées
par @Test, elles font des traitements et vérifient le bon comportement des classes testées par des
méthodes assert***(), toute assertion non vérifiée est signalée comme défaillante. Un cas de test
(TestCase) peut avoir plusieurs sections @Test. Si une section @Test échoue, le TestCase ne
s'arrête pas mais continue sur les sections @Test suivantes (s'il y en a).

import [Link];
import [Link].*;

public class ClasseDeTest extends TestCase {

@BeforeClass
public static void setUpClass() throws Exception {
}

@AfterClass
public static void tearDownClass() throws Exception {
}

@Before
public void setUp() throws Exception {
}

@After
public void tearDown() throws Exception {
}

@Test
public void nomdutest1() {
}

@Test
public void nomdutest2() {
}

}
CONCLUSION

Le Java avancé est un ensemble de fonctionnalités additionnelles, qui vont au-delà des
bases du langage de programmation Java. Il comprend des concepts tels que la programmation
orientée objet, la gestion des threads, la gestion des exceptions, les annotations, les génériques et
les types sauvages, la réflexion, la gestion des flux IO, les expressions lambda, les interfaces
fonctionnelles, les collections, les annotations, les microservices, les frameworks de
développement d'applications web Java comme Spring Framework, Hibernate, etc. Ces concepts
rendent Java un langage de programmation polyvalent pour les développeurs pour le
développement d'applications web, mobiles et serveur.

Java avancée est un véritable défi pour les développeurs Java qui cherchent à se
perfectionner dans le développement Java. Même si le langage Java offre déjà des fonctionnalités
et des outils de développement robustes, le savoir-faire et la connaissance avancés peuvent
augmenter l'efficacité de la programmation et rendre le développement d'applications Java plus
facile, plus efficace et plus sûr.

Vous aimerez peut-être aussi