5.    Les objets en Java


5.1    Définition d'une classe

public class nom
{ // définition des variables membres(variables d'instances) et des méthodes de la classe
}

Exemple:

public class Person {
    public String name = "namePers";
    public int age;
    public Person(){
        name = "name1";
        age = 21;
    }
    public void growUp(int years) {
        age += years;
    }
}

5.2    Instance d'une classe

public class Test {
    public static void main (String[] arg){
        Person p1;
        p1 = new Person();
        Person p2 = new Person();
        p2.growUp(4);
        System.out.println("p1.age: "+p1.age);
        System.out.println("p2.age: "+p2.age);
    }
}

Person p1;
System.out.println("p1.age: "+p1.age);        //erreur Null pointer access


constructeur Person() fait l'initialisation des deux variables membres de l'objet – name et age:
name = "name1";
age = 21;



                Fig. 5.1. Référence et l'objet



5.3    Constructeurs

Person p2 = new Person();


5.4    Construction et initialisation d’un objet

•    une initialisation par défaut de tous les champs de l’objet,
•    une initialisation explicite lors de la déclaration du champ,
•    l’exécution des instructions du corps du constructeur

5.4.1    Initialisation par défaut des champs d’un objet


Type de la variable

Valeur par défaut

boolean

false

char

'\u0000'

entier (byte, short, int, long)

0

float

0.f

double

0.

objet

null

Tab. 5.1 Initialisation par défaut des variables d’un objet


5.4.2    Initialisation explicite des champs d’un objet


public String name = "namePers";


5.4.3    Appel du constructeur

Leconstructeur n’est exécuté qu’après l’initialisation par défaut et l’initialisation explicite.
Les possibilités offertes par le constructeur sont beaucoup plus larges que les initialisations explicites. Sauf cas particulier, il est donc préférable d’effectuer les initialisations dans le constructeur.

5.4.4    Variables membres déclarés avec l’attribut final

public class Ex1 {
    private final int m = 10;
    public static void main(String arg[]){
        Ex1 e1,e2;
        e1=new Ex1();
        e2=new Ex2();
        System.out.println("e1.m = "+e1.m+" e2.m = "+e2.m);
    }
}
e1.m = 10     e2.m = 10


public class Ex2 {
    public Ex2(int arg) {
        m = arg ;
    }
    private final int m ;
    public static void main(String arg[]){
        Ex2 e1,e2;
        e1=new Ex2(3);
        e2=new Ex2(10);
        System.out.println("e1.m= "+e1.m+" e2.m= "+e2.m);
    }
}
e1.m=3 e2.m=10


5.5    L'accès aux membres

private String name = "namePers";
private int age;

public class Test {
    public static void main (String[] arg){
        …
        System.out.println("p1.age: "+p1.age);        //erreur de compilation
        System.out.println("p2.age: "+p2.age);         //erreur de compilation
    }
}


5.6    Membres statiques

public class Person {
    static int num=0;
    public String name = "namePers";
    private int age,nm;
    public Person(){
        nm = ++num;
        name = "name1";
        age = 21;
    }
    public void growUp(int years) {
        age += years;
    }
    public void prt(){
        System.out.println ("\tage :"+age+"\tnumber:"+nm);
    }
}
public class Test {
    public static void main (String[] arg){
        Person p1;
        p1 = new Person();
        Person p2 = new Person();
        p2.growUp(4);
        System.out.print("object p1 - name: "+p1.name);
        p1.prt();
        System.out.print("object p2 - name: "+p2.name);
        p2.prt();
    }
}





              Fig. 5.2 Membre statique - num



5.7    Le ramasse-miettes

class Test {
        static boolean stopNew = false;
        static int created = 0;
        static int finalized = 0;
        static boolean startGarbage= false;      
        int num;
        Test() {
                num = ++created;
                new String("To take up space");
        }
        protected void finalize() {
                if(!startGarbage){
                    System.out.println("Beginning to finalize after " + created
                            + " Tests have been created");
                    startGarbage = true;
                    stopNew = true;
                }
                finalized++;
                if(finalized >= created)
                        System.out.println("All " + finalized + " finalized");
                }
        }
        public class Garbage {
                public static void main(String[] args) {
                while(!Test.stopNew) {
                        new Test();                     
                }
                System.out.println("After all Tests have been created:\n" +
                    "total created = " + Test.created +
                    ", total finalized = " + Test.finalized);
                System.out.println("bye!");
               
        }
}

5.8    Méthodes


5.8.1    Variables locales

Allouées et initialisées explicitement  quand la méthode est appelée et détruites lorsque la méthode se termine

void f(){
    ...
    int var;
    if(condition){
        var= 10;
    }
    var++;            //erreur initialisation
    ...
}


5.8.2    Passage d'arguments


•    par valeur : la méthode reçoit une copie de la valeur de l’argument effectif ; elle travaille sur cette copie qu’elle peut modifier à sa guise, sans que cela n’ait d’incidence sur la valeur de l’argument effectif - types primitifs;
•    par adresse (ou par référence) : la méthode reçoit l’adresse (ou la référence) de l’argument effectif avec lequel elle travaille alors directement ; elle peut donc, le cas échéant, en modifier la valeur - objets.

class Obj {
    int i;
    Obj(){
        i=5;
    }
}
public class Test{
    public static void main(String arg[]){
        int m=4;
        Obj pr = new Obj();
        System.out.println("before: m="+m+"\t pr.i="+pr.i);
        f(m,pr);
        System.out.println("before: m="+m+"\t pr.i="+pr.i);
    }
    static void f(int k, Obj p){
        k++;
        p.i++;
    }
}

5.8.3    La référence this


public void growUp(int years) {
        age += years; 
}

public void growUp(int years) {
        this.age += years; 
}

public class Person {
    String name = "namePers";
    int age;
    public Person(){
        name = "name1";
        age = 21;
    }
    public void growUp(int years) {
        this.age += years; 
    }
}

Si une variable locale et une variable d'instance ont le même nom, la variable locale masque le nom de la variable d'instance

5.8.4    Méthodes statiques


class Stat { .....
    private double x;                            // variable usuelle
    private static int n;                            // variable de classe
    .....
    public void fu() {                            // méthode usuelle
        fs()    ;                                        // appel direct d'une méthode statique
        n = …                                        // accès direct au variable de classe
        x = …                                        // accès direct au variable usuelle
        …
    }
    public static void fs() {                        // méthode de classe
        .....                                         // on ne peut pas accéder directement à x, et à fu()
        n = …                                     // on peut accéder aux variables et aux méthodes de classe
    }
    public static void main(String[] arg) {    // méthode de classe
        Stat objStat;
        …
        fs();                                         // appel directe d'une méthode statique
        objStat.fs();                                 // reste autorisé, mais déconseillé
        objStat.x = …                            // accès au variable usuelle par un objet
        objStat.fu();                                // accès au méthode usuelle par un objet
}

5.9    Affectation et comparaison d’objets

5.9.1    Affectation d’objets

Les affectations entre les objets portent sur les références et non sur les objets eux-mêmes

class Ex{
    int x;
    int y;
    Ex(int x, int y){
        this.x=x;
        this.y = y;
    }
}
public class Affect {
    public static void main(String arg[]){
        Ex a, b;
        a= new Ex(3,5);
        b= new Ex (2,0);
        System.out.print("before assign: ");
        System.out.print ("\t a = ("+a.x+","+a.y+")");
        System.out.println ("\t b = ("+b.x+","+b.y+")");
        a = b;
        System.out.print("after assign: ");
        System.out.print ("\t a = ("+a.x+","+a.y+")");
        System.out.println ("\t b = ("+b.x+","+b.y+")");
    }
}


Fig. 5.3. Les deux objets avant l'affectation



Fig. 5.4. Les deux objets après l'affectation


5.9.2    Comparaison d’objets


Les opérateurs == et != s’appliquent à des objets. Ils portent sur les références.
L’expression a == b est vraie uniquement si a et b font référence à un seul et même objet, et non pas si les valeurs des champs de a et b sont les mêmes.

5.9.3    La notion de clone

On a besoin d'une méthode spéciale pour effectuer la recopie de tous les champs d’un objet dans un autre objet de même type. Toutefois, si les données sont convenablement encapsulées, il n’est pas possible d’y accéder directement. On doit s’appuyer sur l’existence de méthodes d’accès et d’altération de ces variables membres privés. La recopie complète d’un objet nécessiterait une bonne connaissance de son implémentation. Dans certaines situations la copie devient une procédure récursive.


5.10    Surcharge de méthodes

5.10.1    Définition

Surcharge (en anglais overloading) - plusieurs méthodes d'une classe peuvent porter le même nom, à condition que le nombre et le type de leurs arguments permettent au compilateur d’effectuer son choix.

5.10.2    Exemple


public class Person {
    String name;
    int age;
    Person(){
        name = "name 1";
        age = 20;
    }
    Person(String name){
        this.name=name;
        age=20;
    }
    Person(int age){
        name="name 2";
        this.age=age;
    }
    Person(String name, int age){
        this.name=name;
        this.age=age;
    }
    public static void main(String[] arg){
        Person ps[]=new Person[4];
        ps[0]= new Person();
        ps[1]= new Person(22);
        ps[2]= new Person("name 3");
        ps[3]= new Person("name 4",26);
        main(ps);
    }
    public static void main(Person[] ps){
        for(int i=0;i<ps.length;i++){
            System.out.print(ps[i].name);
            System.out.println("   age: "+ps[i].age);
        }
    }
}

5.10.3    Règles générales


Le compilateur recherche toutes les méthodes acceptables et il choisit la meilleure si elle existe. Pour qu’une méthode soit acceptable, il faut:
•    qu’elle dispose du nombre d’arguments voulus;
•    que le type de chaque argument effectif soit compatible par affectation avec le type de l’argument muet correspondant,
•    qu’elle soit accessible;
Le choix de la méthode se déroule suivant les règles:
•    Si aucune méthode n’est acceptable, il y a erreur de compilation.
•    Si une seule méthode est acceptable, elle est utilisée pour l’appel.
•    Si plusieurs méthodes sont acceptables, le compilateur essaie d’en trouver une qui soit meilleure que toutes les autres. Pour ce faire, il procède par éliminations successives.
Après élimination de toutes les méthodes possibles :
•    s’il ne reste plus qu’une seule méthode, elle est utilisée,
•    s’il n’en reste aucune, on obtient une erreur de compilation,
•    s’il en reste plusieurs, on obtient une erreur de compilation mentionnant une ambiguïté.

5.11    Les packages

Un regroupement logique sous un identificateur commun d’un ensemble de tous les classes dans une répertoire. Proche de la notion de bibliothèque.

5.11.1    Utilisation


Une classe peut utiliser toutes les classes de son propre package et toutes les classes publiques des autres packages  
par le nom complet :
java.util.Date today = new java.util.Date();
java.sql.Date deadline = new java.sql.Date();

ou par importer la classe spécifique :
import java.util.Date;

Date today = new Date();

Il est plus simple d'importer toutes les classes du package
import java.util.*;

Date today = new Date();

Attention pour un conflit de nom possible:
import java.util.*
import java.sql.*

Date today;                // erreur

5.11.2    Ajout d'une classe dans un package

Il faut mettre le nom du package en haut du fichier source da la classe, avant tous autre code.

package test.first;
import pers.*;
public class Test {
    public static void main(String[] arg){
        Person ps[]=new Person[4];
        ps[0]= new Person();
        ps[1]= new Person(22);
        ps[2]= new Person("name 3");
        ps[3]= new Person("name 4",26);
        prt(ps);

    }
    public static void prt(Person[] ps){
        for(int i=0;i<ps.length;i++){
            System.out.print(ps[i].getName());
            System.out.println("   age: "+ps[i].getAge());
        }
    }
}

package pers;
public class Person {
    private String name;
    private int age;
    public Person(){
        name = "name 1";
        age = 20;
    }
    public Person(String name){
        this.name=name;
        age=20;
    }
    public Person(int age){
        name="name 2";
        this.age=age;
    }
    public Person(String name, int age){
        this.name=name;
        this.age=age;
    }
    public String getName(){
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
}




Fig.5.5 Schéma des fichiers pour les packages test.first et pers


5.11.3    La visibilité des membres d'une classe


Modifier

class

package

subclass

world

public

X

X

X

X

protected

X

X

X

 

no modifier

X

X

 

 

private

X

 

 

 

Tab 5.2 La visibilité des membres d'une classe


package one;
public class Alpha {
    String name;
    public String fNumber;
    protected int age;
    private String friend;
    public Alpha(){      
    }
    public Alpha(String name,String fn,
                int age,String friend){
        this.name=name;
        fNumber=fn;
        this.age=age;
        this.friend=friend;
    }
    public String getFriend(){
        return friend;
    }
}

package one;
public class Beta {
    public static void main(String [] arg){
        Alpha alpha = new Alpha("Pijo", "1234",19,"Penda");
        System.out.println (alpha.name);
        System.out.println (alpha.fNumber);
        System.out.println (alpha.age);
        //System.out.println (alpha.friend);                            error
        System.out.println (alpha.getFriend());

    }
}

package two;
public class AlphaSub extends one.Alpha{
    AlphaSub(String name,String fn,int age,String friend){
        super(name,fn,age,friend);
    }
    public static void main(String [] arg){
        AlphaSub alpha = new AlphaSub("Pijo","1234",19,"Penda");
        //System.out.println (alpha.name);                    error
        System.out.println (alpha.fNumber);
        System.out.println (alpha.age);                        // accessible
        //System.out.println (alpha.friend);                    error
        System.out.println (alpha.getFriend());
    }
}

package two;
import one.*;
public class Gama {
    public static void main(String [] arg){
        Alpha alpha = new Alpha("Pijo","1234",19,"Penda");
        //System.out.println (alpha.name);                    error
        System.out.println (alpha.fNumber);
        //System.out.println (alpha.age);                        error
        //System.out.println (alpha.friend);                    error
        System.out.println (alpha.getFriend());
    }
}

5.12    Les classes enveloppes


type primitif

classe enveloppe

boolean

Boolean

char

Character

byte

Byte

short

Short

int

Integer

long

Long

float

Float

double

Double

void

Void

BigInteger

BigDecimal

Tab. 5.3 Classes enveloppes

Toutes les classes enveloppes disposent d’un constructeur recevant un argument d’un type primitif :
Character cObj = new Character('d');
Boolean bObj = new Boolean(true);
Float fObj = new Float(4.3f);
int m=4, n=7;
double d = 15.4;
Double dObj = new Double(fp);
Integer iObj = new Integer(m+n);

Méthode de la forme xxxValue (xxx représentant le nom du type primitif) qui permet de retrouver la valeur du type primitif correspondant :
char c = cObj.charValue;
float f = fObj.floatFalue;

emballage (boxing) et débalage (unboxing) :
Character cObj = 'd';
Boolean bObj = new Boolean(true);
float fp = 15.4;
Double dObj = fp+7.3;
char c =cObj;
boolean b = bObj;

Trois raisons pour utiliser une classe enveloppe à la place de type primitif:
•    Comme argument d'une méthode qui veut le modifier;
•    Pour utiliser un constant définit dans la classe. Par exemple MIN_VALUE ou bien MAX_VALUE.
•    Pour utiliser les méthodes pour conversion des types, conversion à partir de strings, et pour conversion entre les systèmes (décimal, octal, hexadécimal, binaire).

public class WrapClass {
    public static void main(String args[]) {
        Boolean b1 = true;
        Boolean b2 = new Boolean("FALSE");
        System.out.print ("The digits in hexadecimal: ");
        for(int j=0;j<16;++j)
            System.out.print(Character.forDigit(j,16)+" ");
        System.out.println();
        int m = Integer.parseInt("254");
        int i = Integer.parseInt("ef",16);
        long ln = Long.parseLong("abcd",16);
        System.out.println(ln + " radix = 10: "+ln);
        System.out.println(ln+" radix=2: "+Long.toString(ln,2));       
        System.out.println(ln+" radix=16: "+Long.toString(ln,16));
        System.out.println("float min value: "+Float.MIN_VALUE);
        System.out.println("double max value: "+Double.MAX_VALUE);
        System.out.println("valeurs boolean: "+ b1+ "\t"+b2);
    }
}

Dans l'exemple suivant on cherche de trouver le premier nombre premier (entier naturel qui admet exactement deux diviseurs distincts entiers et positifs: 1 et lui-même) qui est plus grand d'une certaine valeur. Pour le calcul on utilise la classe enveloppe BigInteger et la méthode isProbablePrime(). La vérification exacte, si un grand nombre est premier ou non, prend trop de temps. La méthode utilisée est plus rapide mais la réponse n'est pas 100% sur. Dans l'exemple l'argument de cette méthode est choisi pour que la possibilité que le nombre soit premier est évalué à 99.21875%.
import java.math.BigInteger; 
public class Big {
    public static void main(String arg[]){
        BigInteger n=new BigInteger("1000000000000");
        BigInteger one=new BigInteger("1");
        while(!n.isProbablePrime(7)) n=n.add(one);
        System.out.println("\n"+n.toString(10)+" is probably prime.");
        System.out.println("It is "+n.bitLength()+" bits in length.");}

}

5.13    La classe Math

public class MathApp {
    public static void main(String args[]) {
        System.out.println(Math.E);
        System.out.println(Math.PI);
        System.out.println(Math.abs(-1234));
        System.out.println(Math.cos(Math.PI/4));
        System.out.println(Math.sin(Math.PI/2));
        System.out.println(Math.tan(Math.PI/4));
        System.out.println(Math.log(1));
        System.out.println(Math.pow(3.1,2.2));   //puissance
        for(int i=0;i<3;++i)
            System.out.print(Math.random()+" ");  // double dans [0, 1.0)
        System.out.println();
        for(int i=0;i<3;++i)
            System.out.print((int)(Math.random()*10)+5+" ");  //entier dans [5, 15)
        System.out.println();
    }
}

5.14    Entrées/sorties sur terminal

variable

type

utilisée pour

in

InputStream

Lecture de l'entrée standard (la console)

out

PrintStream

Affichage sur la sortie standard (la console)

err

PrintStream

Affichage sur la sortie d'erreurs standard (la console)

           Tab. 5.4 Variables statiques de la classe System

La classe Scanner se trouve dans le package java.util et il possède des méthodes simples de lecture des variable de types primitifs de l'entrés standard (System.in)

type de résultat

méthode

Description

Scanner

new Scanner(System.in)

constructeur

BigDecimal

nextBigDecimal()

introduit l'unité suivant de type BigDecimal

BigInteger

nextBigInteger()

introduit l'unité suivant de type BigInteger

BigInteger

nextBigInteger(int radix)

introduit l'unité suivant de type BigInteger en utilisant un système de calcul de la base radix

boolean

nextBoolean()

introduit l'unité suivant de type boolean

byte

nextByte()

introduit l'unité suivant de type byte

byte

nextByte(int radix)

introduit l'unité suivant de type byte en utilisant un système de calcul de la base radix

double

nextDouble()

introduit l'unité suivant de type double

float

nextFloat()

introduit l'unité suivant de type float

int

nextInt()

introduit l'unité suivant de type int

int

nextInt(int radix)

introduit l'unité suivant de type int en utilisant un système de calcul de la base radix

String

nextLine()

introduit l'unité suivant de type String

long

nextLong

introduit l'unité suivant de type long

long

nextLong(int radix)

introduit l'unité suivant de type long en utilisant un système de calcul de la base radix

short

nextShort()

introduit l'unité suivant de type short

short

nextShort(int radix)

introduit l'unité suivant de type short en utilisant un système de calcul de la base radix

             Tab. 5.5 Méthodes utiles de la classe Scanner


import java.util.*;
public class Test{
    public static void main(String[] args)       {
        Scanner in = new Scanner(System.in);
        System.out.print("What is your name? ");
        String name = in.nextLine();                        // get first input
        System.out.print("How old are you? ");
        int age = in.nextInt();                                // get second input
        System.out.println("Hello, " + name + ". Next year, you'll be " + (age + 1)); // display on console
    }
}

Problème avec "end line"
import java.util.*;
public class Test{
    public static void main(String[] args)       {
        Scanner in = new Scanner(System.in);
        System.out.print("How old are you? ");
        int age = in.nextInt();                                // get second input
        in.nextLine();                                        //for the character "end line"
        System.out.print("What is your name? ");
        String name = in.nextLine();                        // get first input
        System.out.println("Hello, " + name + ". Next year, you'll be " + (age + 1)); // display on console
    }
}