java:memoire
Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
java:memoire [2013/11/04 10:03] – [Les types pointeurs] bruno | java:memoire [2013/11/04 16:11] (Version actuelle) – [Java l'amnésique] bruno | ||
---|---|---|---|
Ligne 213: | Ligne 213: | ||
Object[] tabObject = new Object[10]; | Object[] tabObject = new Object[10]; | ||
System.out.println(tabObject); | System.out.println(tabObject); | ||
- | + | ||
+ | Object[] tabObjectCopie = tabObject; | ||
+ | System.out.println(tabObjectCopie); | ||
} | } | ||
Ligne 230: | Ligne 232: | ||
[I@654e3615 | [I@654e3615 | ||
[D@71c0d0a8 | [D@71c0d0a8 | ||
+ | [Ljava.lang.Object; | ||
[Ljava.lang.Object; | [Ljava.lang.Object; | ||
</ | </ | ||
Ligne 243: | Ligne 246: | ||
* l' | * l' | ||
* l' | * l' | ||
+ | |||
On s' | On s' | ||
Ligne 251: | Ligne 255: | ||
| tabDouble | | tabDouble | ||
| tabObject | | tabObject | ||
+ | | tabObjectCopie | ||
+ | |||
+ | :!: Créez d' | ||
+ | |||
+ | Notez toutefois que l' | ||
+ | |||
+ | Notons pour finir ce qui se passe quand on affecte une variable de type construit à une autre variable: | ||
+ | |||
+ | <code java> | ||
+ | |||
+ | Object[] tabObjectCopie = tabObject; | ||
+ | System.out.println(tabObjectCopie); | ||
+ | |||
+ | </ | ||
+ | |||
+ | Java se contente de mettre sa table de correspondance à jour, et ne crée par un nouvel espace mémoire: effectivement, | ||
+ | |||
+ | **Ce type d' | ||
+ | |||
+ | Il diffère de l' | ||
+ | |||
+ | Retenez ceci: | ||
+ | > Seul l' | ||
+ | |||
+ | Autrement dit, lorsqu' | ||
+ | |||
+ | L' | ||
+ | |||
+ | Examinons le code suivant: | ||
+ | |||
+ | <code java> | ||
+ | Object rien = null;//je n' | ||
+ | System.out.println(rien); | ||
+ | </ | ||
+ | |||
+ | L' | ||
+ | Si j' | ||
+ | |||
+ | La figure suivante représente cette affectation dans la mémoire. | ||
+ | {{ : | ||
+ | Seul le premier octet de l' | ||
+ | |||
+ | ==== La mémoire et les tableaux ==== | ||
+ | |||
+ | Vous avez appris qu'un tableau en java ne peut pas changer de taille: il faut lui indiquer combien d' | ||
+ | |||
+ | <code java> | ||
+ | //tableau de 3 entiers | ||
+ | int[] tabInt = new int[3]; | ||
+ | |||
+ | //tableau de 4 Object | ||
+ | Object[] tabObject = new Object[4]; | ||
+ | |||
+ | </ | ||
+ | |||
+ | Pourquoi donc? | ||
+ | |||
+ | Hé bien c'est facile à comprendre. Java va mémoriser la taille du tableau, et pour chaque case va réserver une zone mémoire qui contiendra une adresse vers la donnée. | ||
+ | |||
+ | Les adresses sont en général codées avec 4 octets. Un tableau de 20 éléments aura donc une taille de 20*4 = 80 octets, **quel que soit le type de données qu'il contient**. | ||
+ | Si vous essayez de lire une donnée en dehors de la plage déclarée (par exemple // | ||
+ | |||
+ | Java génère dans ce cas une erreur de type // | ||
+ | |||
+ | Voici quelques exemples illustrés/ | ||
+ | |||
+ | === Tableau de types primitifs === | ||
+ | |||
+ | En mémoire, chaque case du tableau " | ||
+ | |||
+ | L' | ||
+ | |||
+ | La **zone pointée** fait la taille du type primitif (2 octets pour un short, 4 pour un int, etc.). | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | === Tableau de dimension 2, 3, n... === | ||
+ | |||
+ | Soit le code suivant: | ||
+ | <code java> | ||
+ | short [][] tab = new tab[6][4]; | ||
+ | </ | ||
+ | |||
+ | //tab// est un tableau de dimension 6 qui contient des tableaux de dimension 4. | ||
+ | |||
+ | Nous avons vu qu'un tableau est identifié par son adresse en mémoire. | ||
+ | |||
+ | Donc, chacune des 6 cases du tableau //tab// va contenir l' | ||
+ | Chaque tableau de dimension 4 contiendra l' | ||
+ | |||
+ | On devrait donc parler d'un tableau de 6 adresses de tableaux de 4 adresses de short. | ||
+ | |||
+ | {{ : | ||
+ | |||
- | ==== La mémoire des pointeurs ==== | ||
==== Construction d' | ==== Construction d' | ||
+ | Nous avons vu que pour qu'il y ait création, il faut nécessairement que **new** soit utilisé. | ||
+ | |||
+ | Mais que fait cet opérateur **new**? | ||
+ | |||
+ | **new** attend en paramètre un **constructeur** (pas un nom de classe!!!). | ||
+ | |||
+ | Prenons par exemple la hiérarchie de classes suivante: | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Voici le code des classes Animal et Pingouin: | ||
+ | |||
+ | <code java Animal.java> | ||
+ | /** | ||
+ | Une toute petite classe représentant un Animal | ||
+ | | ||
+ | */ | ||
+ | public class Animal{ | ||
+ | /**le nom de l' | ||
+ | private String nom =""; | ||
+ | /** l' | ||
+ | private String espece=""; | ||
+ | |||
+ | /** | ||
+ | Constructeur d' | ||
+ | @param n valeur pour nom | ||
+ | @param e valeur pour espece | ||
+ | */ | ||
+ | public Animal(String n, String e){ | ||
+ | nom = n; | ||
+ | espece = e; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | Constructeur d' | ||
+ | L' | ||
+ | @param n valeur pour nom | ||
+ | */ | ||
+ | public Animal(String n){ | ||
+ | nom = n; | ||
+ | espece = " | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | Pour faire jouer l' | ||
+ | Affiche un petit message en console. | ||
+ | */ | ||
+ | public void jouer(){ | ||
+ | System.out.println(" | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | et Pingouin: | ||
+ | |||
+ | <code java Pingouin.java> | ||
+ | /** Une toute petite classe pour représenter un Pingouin*/ | ||
+ | public class Pingouin extends Animal{ | ||
+ | |||
+ | /** | ||
+ | Constructeur de Pingouin, qui affecte automatiquement la chaîne " | ||
+ | Utilise le constructeur d' | ||
+ | @param n le nom du pingouin | ||
+ | */ | ||
+ | public Pingouin(String n){ | ||
+ | super(n," | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | Ce qui est bien quand on est Pingouin, c'est qu'on peut glisser sur le ventre | ||
+ | */ | ||
+ | public void glisser(){ | ||
+ | System.out.println(" | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | De quels constructeurs disposons-nous? | ||
+ | |||
+ | Classe Object: // | ||
+ | |||
+ | Classe Animal: // | ||
+ | |||
+ | Classe Pingouin: // | ||
+ | |||
+ | Pour construire (créer) un //Object// (une instance d'// | ||
+ | <code java> | ||
+ | Object o1 = new Object(); | ||
+ | Object o2 = new Animal(" | ||
+ | Object o3 = new Animal(" | ||
+ | Object o4 = new Pingouin(" | ||
+ | </ | ||
+ | |||
+ | J'ai utilisé 4 fois l' | ||
+ | |||
+ | Parmi ces instances: | ||
+ | * //o1// pointe vers une instance de type //Object// | ||
+ | * //o2// et //o3// pointent chacune vers une instance de type //Animal// | ||
+ | * //o4// pointe vers une instance de type // | ||
+ | |||
+ | Mais est-ce que pour autant j'ai la même chose en mémoire? | ||
+ | |||
+ | **NON!** | ||
+ | |||
+ | Regardons ce qui se passe en détail. | ||
+ | |||
+ | <code java> | ||
+ | Object o1 = new Object(); | ||
+ | </ | ||
+ | |||
+ | Java va créer une zone de mémoire pour les attributs de //o1//, une autre pour ses méthodes (qui sont celles de la classe Object). | ||
+ | |||
+ | Les attributs (caractéristiques) de chaque instance d' | ||
+ | |||
+ | On peut schématiser cette opération de la manière suivante: | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | <code java> | ||
+ | Object o1 = new Object(); | ||
+ | Object o2 = new Animal(" | ||
+ | </ | ||
+ | |||
+ | Cette fois-ci, on utilise le constructeur de Animal. Un Animal **est un** Object, donc java va commencer par construire un Object en appelant le constructeur // | ||
+ | |||
+ | Ensuite, il va ajouter les attributs de l' | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | <code java> | ||
+ | Object o1 = new Object(); | ||
+ | Object o2 = new Animal(" | ||
+ | Object o3 = new Animal(" | ||
+ | </ | ||
+ | |||
+ | On utilise maintenant le constructeur // | ||
+ | Il n'y a pas de différence avec le cas précédent. Notez cependant que la zone mémoire contenant les méthodes de Animal n'est pas dupliquée (les méthodes/ | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | <code java> | ||
+ | Object o1 = new Object(); | ||
+ | Object o2 = new Animal(" | ||
+ | Object o3 = new Animal(" | ||
+ | Object o4 = new Pingouin(" | ||
+ | </ | ||
+ | |||
+ | Cette fois-ci, le constructeur // | ||
+ | |||
+ | Il suffit alors de " | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Vous vous rappelez du " | ||
+ | |||
+ | C'est le processus de java qui s' | ||
+ | |||
+ | La dernière figure vous permettra de bien comprendre comment il fonctionne: tant qu'une zone de la mémoire reçoit une flèche, ça veut dire qu' | ||
+ | |||
+ | Une zone de mémoire qui ne reçoit plus de flèche est une zone dont les données ne pourront jamais être récupérées: | ||
+ | |||
+ | |||
+ | ==== Et les tableaux d' | ||
+ | |||
+ | Que se passe-t-il dans le cas de tableaux d' | ||
+ | |||
+ | Réfléchissez, | ||
+ | |||
+ | ===== Java l' | ||
+ | |||
+ | Et pour finir, parlons des pertes de mémoires de java! Je veux parler du problème suivant: | ||
+ | |||
+ | <code java> | ||
+ | Object o1 = new Object(); | ||
+ | Object o2 = new Animal(" | ||
+ | Object o3 = new Animal(" | ||
+ | Object o4 = new Pingouin(" | ||
+ | |||
+ | o4.jouer(); | ||
+ | |||
+ | o4.glisser(); | ||
+ | </ | ||
+ | |||
+ | Le compilateur java nous indique deux erreurs: | ||
+ | |||
+ | < | ||
+ | TypeConstruit.java: | ||
+ | o4.jouer(); | ||
+ | ^ | ||
+ | symbol: | ||
+ | location: variable o4 of type Object | ||
+ | TypeConstruit.java: | ||
+ | o4.glisser(); | ||
+ | ^ | ||
+ | symbol: | ||
+ | location: variable o4 of type Object | ||
+ | 2 errors | ||
+ | |||
+ | </ | ||
+ | |||
+ | Pourtant, //o4// a été construit avec un constructeur de // | ||
+ | |||
+ | Rappelez-vous que javac n'est qu'un programme, pas toujours très intelligent donc! | ||
+ | |||
+ | Ce que javac voit, c'est que //o4// est déclaré comme //Object//. | ||
+ | |||
+ | Il cherche donc les méthodes //jouer()// et // | ||
+ | |||
+ | Pourtant, si on regarde le dernier schéma, on voit bien que ces méthodes sont chargées dans la mémoire! | ||
+ | |||
+ | Il va donc falloir explicitement dire à Java de regarder un peu plus en détail. C'est le fameux **cast** ou **transtypage** que nous avons déjà vu avec les types primitifs, sauf que là il ne peut y avoir perte de données: soit l' | ||
+ | |||
+ | Nous allons donc définir une variable et lui dire de pointer dans la même zone mémoire que //o4//, en demandant à java d' | ||
+ | |||
+ | <code java> | ||
+ | Object o4 = new Pingouin(" | ||
+ | /* ça marche pas! | ||
+ | o4.jouer(); | ||
+ | o4.glisser(); | ||
+ | |||
+ | Animal a = (Animal)o4; | ||
+ | a.jouer(); | ||
+ | |||
+ | ((Pingouin)o4).glisser(); | ||
+ | </ | ||
+ | |||
+ | Je vous ai mis deux manières de faire: | ||
+ | * dans le premier cas, je déclare une variable //a//, et après j' | ||
+ | * dans le deuxième cas, je fais directement le cast sans passer par une variable: java créera une variable temporaire et appellera ensuite la méthode glisser(). | ||
+ | |||
+ | Et la question de la fin pour voir si vous avez bien compris: | ||
+ | |||
+ | // | ||
+ | |||
+ | ===== Codes complet des exemples ===== | ||
+ | |||
+ | Archive du code au format tar.gz : {{: | ||
+ | |||
+ | Vous pouvez exporter ce TP en pdf en utilisant le lien du menu de gestion de cette page (le dernier, petite icône en crayon) | ||
java/memoire.1383559432.txt.gz · Dernière modification : 2013/11/04 10:03 de bruno