 |
 |
4) Initialisation & nettoyage |
|
 |
|
Texte original |
 |
Traducteur : F. DEFAIX et Y. CHICHA |
|
 |
///
|
Ce chapitre contient 6 pages
1
2
3
4
5
6
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
What happens if your argument is
bigger than the argument expected by the overloaded method? A
modification of the above program gives the answer:
|
 |
Qu'arrive-t'il lorsque le paramètre est plus grand que celui
attendu par la méthode surchargée ? Une modification du programme précédent donne la réponse
:
|
 |
 |
 |
//: c04:Demotion.java // Demotion of primitives and overloading.
public class Demotion { static void prt(String s) { System.out.println(s); }
void f1(char x) { prt("f1(char)"); } void f1(byte x) { prt("f1(byte)"); } void f1(short x) { prt("f1(short)"); } void f1(int x) { prt("f1(int)"); } void f1(long x) { prt("f1(long)"); } void f1(float x) { prt("f1(float)"); } void f1(double x) { prt("f1(double)"); }
void f2(char x) { prt("f2(char)"); } void f2(byte x) { prt("f2(byte)"); } void f2(short x) { prt("f2(short)"); } void f2(int x) { prt("f2(int)"); } void f2(long x) { prt("f2(long)"); } void f2(float x) { prt("f2(float)"); }
void f3(char x) { prt("f3(char)"); } void f3(byte x) { prt("f3(byte)"); } void f3(short x) { prt("f3(short)"); } void f3(int x) { prt("f3(int)"); } void f3(long x) { prt("f3(long)"); }
void f4(char x) { prt("f4(char)"); } void f4(byte x) { prt("f4(byte)"); } void f4(short x) { prt("f4(short)"); } void f4(int x) { prt("f4(int)"); }
void f5(char x) { prt("f5(char)"); } void f5(byte x) { prt("f5(byte)"); } void f5(short x) { prt("f5(short)"); }
void f6(char x) { prt("f6(char)"); } void f6(byte x) { prt("f6(byte)"); }
void f7(char x) { prt("f7(char)"); }
void testDouble() { double x = 0; prt("double argument:"); f1(x);f2((float)x);f3((long)x);f4((int)x); f5((short)x);f6((byte)x);f7((char)x); } public static void main(String[] args) { Demotion p = new Demotion(); p.testDouble(); } } ///:~
|
 |
//: c04:Demotion.java // Types de base déchus et surcharge.
public class Demotion { static void prt(String s) { System.out.println(s); }
void f1(char x) { prt("f1(char)"); } void f1(byte x) { prt("f1(byte)"); } void f1(short x) { prt("f1(short)"); } void f1(int x) { prt("f1(int)"); } void f1(long x) { prt("f1(long)"); } void f1(float x) { prt("f1(float)"); } void f1(double x) { prt("f1(double)"); }
void f2(char x) { prt("f2(char)"); } void f2(byte x) { prt("f2(byte)"); } void f2(short x) { prt("f2(short)"); } void f2(int x) { prt("f2(int)"); } void f2(long x) { prt("f2(long)"); } void f2(float x) { prt("f2(float)"); }
void f3(char x) { prt("f3(char)"); } void f3(byte x) { prt("f3(byte)"); } void f3(short x) { prt("f3(short)"); } void f3(int x) { prt("f3(int)"); } void f3(long x) { prt("f3(long)"); }
void f4(char x) { prt("f4(char)"); } void f4(byte x) { prt("f4(byte)"); } void f4(short x) { prt("f4(short)"); } void f4(int x) { prt("f4(int)"); }
void f5(char x) { prt("f5(char)"); } void f5(byte x) { prt("f5(byte)"); } void f5(short x) { prt("f5(short)"); }
void f6(char x) { prt("f6(char)"); } void f6(byte x) { prt("f6(byte)"); }
void f7(char x) { prt("f7(char)"); }
void testDouble() { double x = 0; prt("double argument:"); f1(x);f2((float)x);f3((long)x);f4((int)x); f5((short)x);f6((byte)x);f7((char)x); } public static void main(String[] args) { Demotion p = new Demotion(); p.testDouble(); } } ///:~
|
 |
 |
 |
Here, the methods take narrower primitive
values. If your argument is wider then you must
cast to the necessary type using the type name in
parentheses. If you don’t do this, the compiler will issue an error
message.
|
 |
Ici, les méthodes prennent des types de base plus restreints. Si les
paramètres sont d'un type plus grand, if faut les caster (convertir) vers le type requis
en utilisant le nom du type entre parenthèses. Sinon, le compilateur donnera un message
d'erreur.
|
 |
 |
 |
You should be aware that this is a
narrowing conversion, which
means you might lose information during the cast. This is why the compiler
forces you to do it—to flag the narrowing conversion.
|
 |
Il est important de noter qu'il s'agit d'une conversion vers un type plus petit, ce qui signifie que des informations
peuvent être perdues pendant la conversion. C'est d'ailleurs pour cette raison que le compilateur
force une conversion explicite.
|
 |
 |
 |
Overloading on return
values
|
 |
Surcharge sur la valeur de retour
|
 |
 |
 |
It is common to wonder “Why only
class names and method argument lists? Why not distinguish between methods based
on their return values?” For example, these two methods, which have the
same name and arguments, are easily distinguished from each
other:
|
 |
Il est fréquent de se demander «Pourquoi seulement les noms de classes et
la liste des paramètres des méthodes ? Pourquoi ne pas aussi distinguer entre deux méthodes en
se basant sur leur type de retour ?» Par exemple, ces deux méthodes, qui ont le même nom et
les mêmes arguments, peuvent facilement être distinguées l'une de l'autre :
|
 |
 |
 |
void f() {} int f() {}
|
 |
void f() {} int f() {}
|
 |
 |
 |
This works fine when the compiler can
unequivocally determine the meaning from the context, as in int x =
f( ). However, you can call a method and ignore the return value; this
is often referred to as calling a method for its side
effect since you don’t care about the return value but instead want
the other effects of the method call. So if you call the method this
way:
|
 |
Cela fonctionne bien lorsque le compilateur peut déterminer le sens sans
équivoque depuis le contexte, comme dans int x = f( ). Par contre, on peut
utiliser une méthode et ignorer sa valeur de retour. On se réfère souvent à cette action comme
appeler une méthode pour ses effets de bord puisqu'on ne s'intéresse pas à la valeur de
retour mais aux autres effets que cet appel de méthode génère. Donc, si on appelle la méthode comme
suit :
|
 |
 |
 |
f();
|
 |
f();
|
 |
 |
 |
how can Java determine which f( ) should be called? And how could someone reading the code see it? Because of this sort of problem, you cannot use return value types to distinguish overloaded methods.
|
 |
Comment Java peut-il déterminer quelle méthode f( )
doit être exécutée ? Et comment quelqu'un lisant ce code pourrait-il le savoir ? A cause
de ce genre de difficultés, il est impossible d'utiliser la valeur de retour pour différencier deux
méthodes Java surchargées.
|
 |
 |
 |
Default constructors
|
 |
Constructeurs par défaut
|
 |
 |
 |
As mentioned previously, a default constructor (a.k.a. a “no-arg” constructor) is one without arguments, used to create a “vanilla object.” If you create a class that has no constructors, the compiler will automatically create a default constructor for you. For example:
|
 |
Comme mentionné précédemment, un constructeur par défaut (c.a.d un
constructeur «no-arg» ) est un constructeur sans argument, utilisé pour créer des « objets de base
». Si une classe est créée sans constructeur, le compilateur créé automatiquement un constructeur
par défaut. Par exemple :
|
 |
 |
 |
//: c04:DefaultConstructor.java
class Bird { int i; }
public class DefaultConstructor { public static void main(String[] args) { Bird nc = new Bird(); // default! } } ///:~
|
 |
//: c04:DefaultConstructor.java
class Bird { int i; }
public class DefaultConstructor { public static void main(String[] args) { Bird nc = new Bird(); // défaut ! } } ///:~
|
 |
 |
 |
The line
|
 |
La ligne
|
 |
 |
 |
new Bird();
|
 |
new Bird();
|
 |
 |
 |
creates a new object and calls the default constructor, even though one was not explicitly defined. Without it we would have no method to call to build our object. However, if you define any constructors (with or without arguments), the compiler will not synthesize one for you:
|
 |
crée un nouvel objet et appelle le constructeur par défaut, même s'il
n'était pas défini explicitement. Sans lui, il n'y aurait pas de méthode à appeler pour créer cet
objet. Par contre, si au moins un constructeur est défini (avec ou sans argument), le compilateur
n'en synthétisera pas un :
|
 |
 |
 |
class Bush { Bush(int i) {} Bush(double d) {} }
|
 |
class Bush { Bush(int i) {} Bush(double d) {} }
|
 |
 |
 |
Now if you say:
|
 |
Maintenant si on écrit :
|
 |
 |
 |
new Bush();
|
 |
new Bush();
|
 |
 |
 |
the compiler will complain that it cannot find a constructor that matches. It’s as if when you don’t put in any constructors, the compiler says “You are bound to need some constructor, so let me make one for you.” But if you write a constructor, the compiler says “You’ve written a constructor so you know what you’re doing; if you didn’t put in a default it’s because you meant to leave it out.”
|
 |
le compilateur donnera une erreur indiquant qu'aucun constructeur ne
correspond. C'est comme si lorsqu'aucun constructeur n'est fourni, le compilateur dit «Il faut un
constructeur, je vais en créer un.» Alors que s'il existe un constructeur, le compilateur dit «Il y
a un constructeur donc le développeur sait se qu'il fait; s'il n'a pas défini de constructeur par
défaut c'est qu'il ne désirait pas qu'il y en ait un.»
|
 |
 |
 |
The this keyword
|
 |
Le mot-clé this
|
 |
 |
 |
If you have two objects of the same type called a and b, you might wonder how it is that you can call a method f( ) for both those objects:
|
 |
Lorsqu'il existe deux objets a et b du
même type , il est intéressant de se demander comment on peut appeler une méthode
f( ) sur ces deux objets :
|
 |
 |
 |
class Banana { void f(int i) { /* ... */ } } Banana a = new Banana(), b = new Banana(); a.f(1); b.f(2);
|
 |
class Banana { void f(int i) { /* ... */ } } Banana a = new Banana(), b = new Banana(); a.f(1); b.f(2);
|
 |
 |
 |
If there’s only one method called
f( ), how can that method know whether it’s being called for
the object a or b?
|
 |
S'il y a une unique méthode f( ), comment cette
méthode peut-elle savoir si elle a été appelée sur l'objet a ou
b ?
|
 |
 |
 |
To allow you to write the code in a
convenient object-oriented syntax in which you “send a message to an
object,” the compiler does some undercover work for you. There’s a
secret first argument passed to the method f( ), and that argument
is the reference to the object that’s being manipulated. So the two method
calls above become something like:
|
 |
Pour permettre au développeur d'écrire le code dans une syntaxe pratique et
orienté objet dans laquelle on «envoie un message vers un objet,» le compilateur effectue un
travail secret pour le développeur. Il y a un premier paramètre caché passé à la méthode
f( ), et ce paramètre est une référence vers l'objet en train d'être
manipulé. Les deux appels de méthode précédents correspondent donc à ceci :
|
 |
 |
 |
Banana.f(a,1); Banana.f(b,2);
|
 |
Banana.f(a,1); Banana.f(b,2);
|
 |
 |
 |
This is internal and you can’t
write these expressions and get the compiler to accept them, but it gives you an
idea of what’s happening.
|
 |
Ce travail est interne et il est impossible d'écrire des expressions de ce
type directement en espérant que le compilateur les acceptera, mais cela donne une idée de ce qui
se passe.
|
 |
 |
 |
Suppose you’re inside a method and
you’d like to get the reference to the current object. Since that
reference is passed secretly by the compiler, there’s no identifier
for it. However, for this purpose there’s a keyword: this. The
this keyword—which can be used only inside a method—produces
the reference to the object the method has been called for. You can treat this
reference just like any other object reference. Keep in mind that if
you’re calling a method of your class from within another method of your
class, you don’t need to use this; you simply call the method. The
current this reference is automatically used for the other method. Thus
you can say:
|
 |
Supposons maintenant que l'on est à l'intérieur d'une méthode et que l'on
désire obtenir une référence sur l'objet courant. Comme cette référence est passée en tant que
paramètre caché par le compilateur, il n'y a pas d'identificateur pour elle. Cette pour
cette raison que le mot clé this existe. this - qui ne peut être
utilisé qu'à l'intérieur d'une méthode - est une référence sur l'objet pour lequel cette méthode à
été appelée. On peut utiliser cette référence comme tout autre référence vers un objet. Il n'est
toutefois pas nécessaire d'utiliser this pour appeler une méthode de la classe
courante depuis une autre méthode de la classe courante ; il suffit d'appeler cette méthode.
La référence this est automatiquement utilisée pour l'autre méthode. On peut
écrire :
|
 |
 |
 |
class Apricot { void pick() { /* ... */ } void pit() { pick(); /* ... */ } }
|
 |
class Apricot { void pick() { /* ... */ } void pit() { pick(); /* ... */ } }
|
 |
 |
 |
Inside pit( ), you
could say this.pick( ) but there’s no need to. The
compiler does it for you automatically. The this keyword is used only for
those special cases in which you need to explicitly use the reference to the
current object. For example, it’s often used in return statements
when you want to return the reference to the current object:
|
 |
A l'intérieur de pit( ), on pourrait écrire
this.pick( ) mais ce n'est pas nécessaire. Le compilateur le fait
automatiquement pour le développeur. Le mot-clé this est uniquement utilisé pour
les cas spéciaux dans lesquels on doit utiliser explicitement une référence sur l'objet courant.
Par exemple, il est courament utilisé en association avec return quand on désire
renvoyer une référence sur l'objet courant :
|
 |
 |
 |
//: c04:Leaf.java // Simple use of the "this" keyword.
public class Leaf { int i = 0; Leaf increment() { i++; return this; } void print() { System.out.println("i = " + i); } public static void main(String[] args) { Leaf x = new Leaf(); x.increment().increment().increment().print(); } } ///:~
|
 |
//: c04:Leaf.java // Utilisation simple du mot-clé "this".
public class Leaf { int i = 0; Leaf increment() { i++; return this; } void print() { System.out.println("i = " + i); } public static void main(String[] args) { Leaf x = new Leaf(); x.increment().increment().increment().print(); } } ///:~
|
 |
 |
 |
Because increment( ) returns
the reference to the current object via the this keyword, multiple
operations can easily be performed on the same object.
|
 |
Puisque increment( ) renvoie une référence vers
l'objet courant par le biais du mot-clé this, on peut facilement appeler plusieurs
opérations successivement sur le même objet.
|
 |
 |
 |
Calling constructors from constructors
|
 |
Appeler un constructeur depuis un autre constructeur
|
 |
 |
 |
When you write several constructors for a
class, there are times when you’d like to call one constructor from
another to avoid duplicating code. You can do this using the this
keyword.
|
 |
Quand une classe possède plusieurs constructeurs, il peut être utile
d'appeler un constructeur depuis un autre pour éviter de la duplication de code. C'est possible
grâce au mot-clé this.
|
 |
 |
 |
Normally, when you say this, it is
in the sense of “this object” or “the current object,”
and by itself it produces the reference to the current object. In a constructor,
the this keyword takes on a different meaning when you give it an
argument list: it makes an explicit call to the constructor that matches that
argument list. Thus you have a straightforward way to call other
constructors:
|
 |
En temps normal, this signifie «cet objet» ou «l'objet
courant,» et renvoie une référence sur l'objet courant. Dans un constructeur, le mot-clé
this prend un sens différent quand on lui passe une liste de paramètres : il
signifie un appel explicite au constructeur qui correspond à cette liste de paramètres. Cela donne
un moyen très simple d'appeler d'autres constructeurs :
|
 |
 |
 |
//: c04:Flower.java // Calling constructors with "this."
public class Flower { int petalCount = 0; String s = new String("null"); Flower(int petals) { petalCount = petals; System.out.println( "Constructor w/ int arg only, petalCount= " + petalCount); } Flower(String ss) { System.out.println( "Constructor w/ String arg only, s=" + ss); s = ss; } Flower(String s, int petals) { this(petals); //! this(s); // Can't call two! this.s = s; // Another use of "this" System.out.println("String & int args"); } Flower() { this("hi", 47); System.out.println( "default constructor (no args)"); } void print() { //! this(11); // Not inside non-constructor! System.out.println( "petalCount = " + petalCount + " s = "+ s); } public static void main(String[] args) { Flower x = new Flower(); x.print(); } } ///:~
|
 |
//: c04:Flower.java // Appel de constructeurs avec "this."
public class Flower { int petalCount = 0; String s = new String("null"); Flower(int petals) { petalCount = petals; // Constructeur avec un unique paramètre int System.out.println( "Constructor w/ int arg only, petalCount= " + petalCount); } Flower(String ss) { // Constructeur avec un unique paramètre String System.out.println( "Constructor w/ String arg only, s=" + ss); s = ss; } Flower(String s, int petals) { this(petals); //! this(s); // Impossible d'en appeler deux ! this.s = s; // Autre usage de "this" System.out.println("String & int args"); }
// Constructeur par défaut Flower() { this("hi", 47); System.out.println( "default constructor (no args)"); } void print() { //! this(11); // Pas à l'intérieur d'une méthode normale ! System.out.println( "petalCount = " + petalCount + " s = "+ s); } public static void main(String[] args) { Flower x = new Flower(); x.print(); } } ///:~
|
 |
 |
 |
The constructor Flower(String s, int
petals) shows that, while you can call one constructor using this,
you cannot call two. In addition, the constructor call must be the first thing
you do or you’ll get a compiler error message.
|
 |
Le constructeur Flower(String s, int petals) montre qu'on
peut appeler un constructeur en utilisant this, mais pas deux. De plus, l'appel au
constructeur doit absolument être la première instruction sinon le compilateur donnera un message
d'erreur.
|
 |
 |
 |
This example also shows another way
you’ll see this used. Since the name of the argument s and
the name of the member data s are the same, there’s an ambiguity.
You can resolve it by saying this.s to refer to the member data.
You’ll often see this form used in Java code, and it’s used in
numerous places in this book.
|
 |
Cet exemple montre aussi un usage différent du mot-clé
this. Les noms du paramètre s et du membre de données
s étant les mêmes, il y a ambiguïtée. On la résoud en utilisant
this.s pour se référer au membre de donnés. Cette forme est très courante en Java
et utilisée fréquemment dans ce livre.
|
 |
 |
 |
In print( ) you can see that
the compiler won’t let you call a constructor from inside any method other
than a constructor.
|
 |
Dans la méthode print( ) on peut voir que le
compilateur ne permet pas l'appel d'un constructeur depuis toute autre méthode qu'un
constructeur.
|
 |
 |
 |
The meaning of static
|
 |
La signification de static
|
 |
 |
 |
With the this keyword in mind, you
can more fully understand what it means to make a
method static. It means
that there is no this for that particular method. You cannot call
non-static methods from inside static
methods[28]
(although the reverse is possible), and you can call a static method for
the class itself, without any object. In fact, that’s primarily what a
static method is for. It’s as if you’re creating the
equivalent of a global function (from C). Except global functions are not
permitted in Java, and putting the static method inside a class allows it
access to other static methods and to static
fields.
|
 |
En pensant au mot-clé this, on comprend mieux le sens de
rendre une méthode statics. Cela signifie qu'il n'y a pas de this
pour cette méthode. Il est impossible d'appeler une méthode non-static depuis une
méthode static [28] (par contre, l'inverse est
possible), et il est possible d'appeler une méthode static sur la classe
elle-même, sans aucun objet. En fait, c'est principalement la raison de l'existence des méthodes
static. C'est l'équivalent d'une fonction globale en C. Sauf que les fonctions
globales sont interdites en Java, et ajouter une méthode static dans une classe
lui permet d'accéder à d'autres méthodes static ainsi qu'aux membres
static.
|
 |
 |
 |
Some people argue that static
methods are not object-oriented since they do have the semantics of a global
function; with a static method you don’t send a message to an
object, since there’s no this. This is probably a fair argument,
and if you find yourself using a lot of static methods you should
probably rethink your strategy. However, statics are pragmatic and there
are times when you genuinely need them, so whether or not they are “proper
OOP” should be left to the theoreticians. Indeed, even
Smalltalk has the equivalent in its “class
methods.”
|
 |
Certaines personnes argumentent que les méthodes static ne
sont pas orientées objet puisqu'elles ont la sémantique des fonctions globales ; avec une
méthode static on n'envoie pas un message vers un objet, puisqu'il n'y a pas de
this. C'est probablement un argument valable, et si vous utilisez
beaucoup de méthodes statiques vous devriez repenser votre stratégie. Pourtant, les
méthodes statics sont utiles et il y a des cas où on en a vraiment besoin. On peut
donc laisser les théoriciens décider si oui ou non il s'agit de vraie programmation orientée objet.
D'ailleurs, même Smalltalk a un équivalent avec ses «méthodes de classe.»
|
 |
 |
 |
Cleanup: finalization and garbage collection
|
 |
Nettoyage : finalisation et ramasse-miettes
|
 |
 |
 |
Programmers know about the importance of
initialization, but often forget the importance of cleanup. After all, who needs
to clean up an int? But with libraries, simply “letting go”
of an object once you’re done with it is not always safe. Of course, Java
has the garbage collector to reclaim the memory of
objects that are no longer used. Now consider a very unusual case. Suppose your
object allocates “special” memory without using
new. The garbage collector knows only how to
release memory allocated with new, so it won’t know how to
release the object’s “special” memory. To handle this case,
Java provides a method called finalize( )
that you can define for your class. Here’s how it’s supposed
to work. When the garbage collector is ready to release the storage used for
your object, it will first call finalize( ), and only on the next
garbage-collection pass will it reclaim the object’s memory. So if you
choose to use finalize( ), it gives you the ability to perform some
important cleanup at the time of garbage collection.
|
 |
Les programmeurs connaissent l'importance de l'initialisation mais oublient
souvent celle du nettoyage. Après tout, qui a besoin de nettoyer un int ?
Cependant, avec des bibliothèques, simplement oublier un objet après son utilisation n'est pas
toujours sûr. Bien entendu, Java a un ramasse-miettes pour récupérer la mémoire prise par des
objets qui ne sont plus utilisés. Considérons maintenant un cas très particulier. Supposons que
votre objet alloue une zone de mémoire spéciale sans utiliser new. Le
ramasse-miettes ne sait récupérer que la mémoire allouée avec new, donc
il ne saura pas comment récupérer la zone «speciale» de mémoire utilisée par l'objet. Pour gérer ce
cas, Java fournit une méthode appelée finalize( ) qui peut être définie dans
votre classe. Voici comment c'est supposé marcher. Quand le ramasse-miettes est prêt à
libérer la mémoire utilisée par votre objet, il va d'abord appeler
finalize( ) et ce n'est qu'à la prochaine passe du ramasse-miettes que la
mémoire de l'objet est libérée. En choisissant d'utiliser finalize( ), on a
la possibilité d'effectuer d'importantes tâches de nettoyage à l'exécution du
ramasse-miettes.
|
 |
 |
 |
This is a potential programming pitfall
because some programmers, especially C++ programmers, might initially mistake
finalize( ) for the destructor in C++,
which is a function that is always called when an object is destroyed. But it is
important to distinguish between C++ and Java here, because in C++ objects
always get destroyed (in a bug-free program), whereas in Java objects do not
always get garbage-collected. Or, put another way:
|
 |
C'est un piège de programmation parce que certains programmeurs,
particulièrement les programmeurs C++, risquent au début de confondre
finalize( ) avec le destructeur de C++ qui est une fonction toujours
appelée quand un objet est détruit. Cependant il est important ici de faire la différence entre C++
et Java, car en C++ les objets sont toujours détruits (dans un programme sans bug), alors
qu'en Java les objets ne sont pas toujours récupérés par le ramasse-miettes. Dit
autrement :
|
 |
 |
 |
Garbage collection is not
destruction.
|
 |
Le mécanisme de ramasse-miettes n'est pas un mécanisme de
destruction.
|
 |
 |
 |
If you remember this, you will stay out
of trouble. What it means is that if there is some activity that must be
performed before you no longer need an object, you must perform that activity
yourself. Java has no destructor or similar concept, so you must create an
ordinary method to perform this cleanup. For example, suppose in the process of
creating your object it draws itself on the screen. If you don’t
explicitly erase its image from the screen, it might never get cleaned up. If
you put some kind of erasing functionality inside finalize( ), then
if an object is garbage-collected, the image will first be removed from the
screen, but if it isn’t, the image will remain. So a second point to
remember is:
|
 |
Si vous vous souvenez de cette règle de base, il n'y aura pas de problème.
Cela veut dire que si une opération doit être effectuée avant la disparition d'un objet, celle-ci
est à la charge du développeur. Java n'a pas de mécanisme équivalent au destructeur, il est donc
nécessaire de créer une méthode ordinaire pour réaliser ce nettoyage. Par exemple, supposons qu'un
objet se dessine à l'écran pendant sa création. Si son image n'est pas effacée explicitement de
l'écran, il se peut qu'elle ne le soit jamais. Si l'on ajoute une fonctionnalité d'effacement dans
finalize( ), alors l'image sera effacée de l'écran si l'objet est récupéré
par le ramasse-miette, sinon l'image restera. Il y a donc une deuxième règle à se rappeler
:
|
 |
 |
 |
Your objects might not get
garbage-collected.
|
 |
Les objets peuvent ne pas être récupérés par le
ramasse-miettes.
|
 |
 |
 |
You might find that the storage for an
object never gets released because your program never nears the point of running
out of storage. If your program completes and the garbage collector never gets
around to releasing the storage for any of your objects, that storage will be
returned to the operating system en masse as the program exits. This is a
good thing, because garbage collection has some overhead, and if you never do it
you never incur that
expense.
|
 |
Il se peut que la mémoire prise par un objet ne soit jamais libérée parce
que le programme n'approche jamais la limite de mémoire qui lui a été attribuée. Si le programme se
termine sans que le ramasse-miettes n'ait jamais libéré la mémoire prise par les objets, celle-ci
sera rendue en masse (NDT : en français dans le texte) au système d'exploitation au
moment où le programme s'arrête. C'est une bonne chose, car le ramasse-miettes implique un coùt
supplémentaire et s'il n'est jamais appelé, c'est autant d'économisé.
|
 |
 |
 |
What is finalize( ) for?
|
 |
A quoi sert finalize( ) ?
|
 |
 |
 |
You might believe at this point that you
should not use finalize( ) as a general-purpose cleanup method. What
good is it?
|
 |
A ce point, on peut croire qu'il ne faudrait pas utiliser
finalize( ) comme méthode générale de nettoyage. A quoi sert-elle
alors ?
|
 |
 |
 |
A third point to remember
is:
|
 |
Une troisième règle stipule :
|
 |
 |
 |
Garbage collection is only about
memory.
|
 |
Le ramasse-miettes ne s'occupe que de la mémoire.
|
 |
 |
 |
That is, the sole reason for the
existence of the garbage collector is to recover memory that your program is no
longer using. So any activity that is associated with garbage collection, most
notably your finalize( ) method, must also be only about memory and
its deallocation.
|
 |
C'est à dire que la seule raison d'exister du ramasse-miettes est de
récupérer la mémoire que le programme n'utilise plus. Par conséquent, toute activité associée au
ramasse-miettes, la méthode finalize( ) en particulier, doit se concentrer
sur la mémoire et sa libération.
|
 |
 |
 |
Does this mean that if your object
contains other objects finalize( ) should explicitly release those
objects? Well, no—the garbage collector takes care of the release of all
object memory regardless of how the object is created. It turns out that the
need for finalize( ) is limited to special cases, in which your
object can allocate some storage in some way other than creating an object. But,
you might observe, everything in Java is an object so how can this
be?
|
 |
Est-ce que cela veut dire que si un objet contient d'autres objets,
finalize( ) doit libérer ces objets explicitement ? La réponse est...
non. Le ramasse-miettes prend soin de libérer tous les objets quelle que soit la façon dont ils ont
été créés. Il se trouve que l'on a uniquement besoin de finalize( ) dans des
cas bien précis où un objet peut allouer de la mémoire sans créer un autre objet. Cependant vous
devez vous dire que tout est objet en Java, donc comment est-ce possible ?
|
 |
 |
 |
It would seem that
finalize( ) is in place because of the possibility that you’ll
do something C-like by allocating memory using a mechanism other than the normal
one in Java. This can happen primarily through native methods, which are
a way to call non-Java code from Java. (Native methods are discussed in Appendix
B.) C and C++ are the only languages currently supported by native methods, but
since they can call subprograms in other languages, you can effectively call
anything. Inside the non-Java code, C’s malloc( ) family of
functions might be called to allocate storage, and unless you call
free( ) that storage will not be released, causing a memory leak. Of
course, free( ) is a C and C++ function, so you’d need to call
it in a native method inside your finalize( ).
|
 |
Il semblerait que finalize( ) ait été introduit parce
qu'il est possible d'allouer de la mémoire à-la-C en utilisant un mécanisme autre que celui proposé
normalement par Java. Cela arrive généralement avec des méthodes natives, qui sont une
façon d'appeler du code non-Java en Java (les méthodes natives sont expliquées en Appendice B). C
et C++ sont les seuls langages actuellement supportés par les méthodes natives, mais comme elles
peuvent appeler des routines écrites avec d'autres langages, il est en fait possible d'appeler
n'importe quoi. Dans ce code non-Java, on peut appeler des fonctions de la famille de
malloc( ) en C pour allouer de la mémoire, et à moins qu'un appel à
free( ) ne soit effectué cette mémoire ne sera pas libérée, provoquant une
«fuite». Bien entendu, free( ) est une fonction C et C++, ce qui veut dire
qu'elle doit être appelée dans une méthode native dans le finalize( )
correspondant.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |