1.2- Vie et mort des objets en Java


1 Types Objets-Classes

2 Des élements plus généraux sur l'organisation des classes, la compilation etc...

3 Le cas de String : non pas un type primitif mais une classe avec des abus de syntaxes

4 Un point sur les variables et les méthodes


1 Types Objets-Classes



Forme d'une classe

Le fichier
HelloWorld.java est une classe, une classe particulière puisqu'elle contient un main, c'est à dire qu'elle peut être à la base d'une exécution.

Une classe s'organise comme une série d'attributs et une série de méthodes, dans un ordre libre. On va travailler sur une classe Facture qui pourrait être créée dans le cadre d'une application comptable. Le fichier Facture.java doit être organisé ainsi :

public class Facture{
}
On définit une classe Facture ici. On va la doter d'une fonction main pour pouvoir lancer l'exécution à partir de cette classe.

public class Facture{

public static void main(String[] args)
	{
	}
}


On définit des attributs pour la classe. Par exemple, pour une Facture, on a un montant, un nombre d'objets vendus et un prix unitaire :
public class Facture{

	public double montant;
	
	public int nbVentes;

	public double prixUnitaire;

public static void main(String[] args)
	{
	}
}


Par ailleurs, pour une facture, on peut vouloir calculer le montant, diminuer le montant d'une remise : ce sont des comportements qui vont être implementées sous forme de méthodes :
public class Facture{

	public double montant;
	
	public int nbUniteesVendues;

	public double prixUnitaire;


/*La méthode qui renvoie le Chiffre d'affaire généré par la facture*/
public double calculCA()
	{
	return prixUnitaire*nbUniteesVendues;
	}


/*La méthode qui diminue le montant d'une remise*/
public void integrationRemise(double taux)
	{
		if(taux>=0 && taux<=1)
		{
		montant=montant-taux*montant;
		}
	}

public void calculMontant()
	{
	montant=nbUniteesVendues*prixUnitaire;
	}
	
/*une méthode particulière : main*/
public static void main(String[] args)
	{
	}

}
On constate que la déclaration d'un attribut pour la classe est de la forme :

[Visbilité] type nom

on peut également combiner avec une déclaration

[Visbilité] type nom = valeur

Par exemple :
public int n;
private double a=0.7;


Pour la définition d'une méthode :

[Visbilité] type_du_retour nom_méthode(paramètres)

Par exemple :
public void calculMontant(double remise){}

private double produit(double a,int b)
	{
	return a*b;
	}

public double produit(double a,double b)
	{
	return a*b;
	}

public int max(int n,int m)
	{
		if(n>m)
		return n;
		else
		return m;
	}

On peut mettre un type void de retour, dans ce cas, il n'y a pas à indiquer d'instruction de retour. Dans le cas où le type de retour n'est pas void : il faut spécifier une instruction de retour, ce peut être une variable return x; ou bien return x*2; etc... Il faut bien sûr que le type de la variable de retour soit le type retourné par la méthode.

Un attribut ou une méthode de visibilité private ne sont utilisables qu'au sein des méthodes de la classe dans laquelle ils sont définis. Au contraire, les attributs et les méthodes public sont utilisables sans restriction.



new : instanciation et constructeur

Il reste à créer des objets, instances de la classe qu'on vient de créer et à manipuler les méthodes et les variables. La création d'un objet se fait par la création et l'appel particulier d'une méthode particulière le constructeur, celui-ci est une méthode du nom de la classe, ne définit pas un type de retour, a pour retour un objet de type Facture et n'est pas appellée comme les autres méthodes, son appel passant par le mot clé new

public class Facture{

	public double montant;
	
	public int nbUniteesVendues;

	public double prixUnitaire;


/*La méthode qui renvoie le Chiffre d'affaire généré par la facture*/
public double calculCA()
	{
	return prixUnitaire*nbUniteesVendues;
	}


/*La méthode qui diminue le montant d'une remise*/
public void integrationRemise(double taux)
	{
		if(taux>=0 && taux<=1)
		{
		montant=montant-taux*montant;
		}
	}

public void calculMontant()
	{
	montant=nbUniteesVendues*prixUnitaire;
	}


/*Le constructeur*/
public Facture(int nbUnites,double prix)
	{
	nbUniteesVendues=nbUnites;
	prixUnitaire=prix;
	montant=0;
	}

/*une méthode particulière : main*/
public static void main(String[] args)
	{
	Facture fact1,fact2;/*Declaration de variables de type Facture*/

	fact1=new Facture(7,1.6);/*Instanciation en appelant le constructeur et affectation à la variable fact1*/

	fact2=new Facture(3,10);/*Instanciation en appelant le constructeur et affectation à la variable fact2*/
	}

}
On a ici crée un constructeur Facture qui affecte les variables nbUnites et prix. Dans la méthode main, on déclare des variables de type Facture : fact1 et fact2 et on ensuite, on crée des objets qu'on affecte à des variables.

Un constructeur peut, à l'instar des autres méthodes, prendre un ou plusieurs paramètres en entrée et les manipuler pour initialiser les variables-attibuts de l'objet.

Manipulation des attributs et des méthodes

Créer une classe, c'est créer un type pour lequel on peut ensuite déclarer des variables, instancier des objets et affecter des objets instanciés à des variables. Soit une variables affectée d'un objet Facture, à ce moment, il est possible de faire des opérations sur les attributs de l'objet et d'en appeller les méthodes. Quelques exemples :

Facture fact1=new Facture(4,5.5);
System.out.println(fact1.nbUniteesVendues);	/*Affiche 4*/
System.out.println(fact1.prixUnitaire);		/*Affiche 5.5*/
System.out.println(fact1.montant);		/*Affiche 0*/
double x=fact1.calculCA();			/*On affecte 5.5*4=>22 à x*/
System.out.println(x);				/*Affichage de 22*/

System.out.println(fact1.montant);		/*Affichage de 0*/
fact1.calculMontant();				/*Appel de la méthode de calcul du montant*/
System.out.println(fact1.montant);		/*Afiichage de montant : 22*/
fact1.integrationRemise(0.05);			/*Appel de la méthode d'intégration de la remise sur le montant*/
System.out.println(fact1.montant);		/*Affichage de la nouvelle valeur du montant : 20.9*/
Pour récupérer l'ensemble de cette classe et l'exécuter : Facture.java. Le lecteur est inviter à rajouter des instructions comme celle de l'exemple précédent dans le main() pour tester sa compréhension du fonctionnnement de la manipulation des attributs et des méthodes des objets. Note : il est possible de définir les attributs et les méthodes dans n'importe quel ordre.

Réference

Quand on manipule une variable de type int (de type primitif), on manipule une valeur, quand on manipule une variable d'un type objet, on manipule une réference, une adresse en mémoire (voir le chapitre 0.2). Soit le code suivant :

int i=5;
int j;
j=i;
j=j+1;
System.out.println(i+" "+j);

Facture fact1=new Facture(4,3);
Facture fact2;
fact2=fact1;
fact2.nbUniteesVendues=fact2.nbUniteesVendues+1;
System.out.println(fact1.nbUniteesVendues+" "+fact2.nbUniteesVendues);


Ici, le premier affichage donnera "5 6" : l'augmentation de la valeur de j n'a pas modifié la valeur de i malgré l'instruction j=i qui ne constitue qu'une copie de valeur. Le deuxième affichage donne "5 5" : le phénomène est différents. Pour le cas des types primitifs, l'instruction j=i correspond à la copie de la case mémoire associée à i dans la case mémoire associée à j, il se passe la même chose pour fact2=fact1, mais le contenu d'une variable de type Facture, c'est l'adresse des variables-attributs, donc en manipulant fact2.nbUniteesVendues, on manipule fact1.nbUniteesVendues. Vous pouvez faire un schéma pour vous en rendre compte. Si enfin on fait :

fact2.nbUniteesVenduees=6;
i=fact2.nbUniteesVendues;
fact2.nbUniteesVendues=fact2.nbUniteesVendues+1;
System.out.println(i+" "+fact2.nbUniteesVendues);


Ici l'affichage donne "6 7". i=fact2.nbUniteesVendues est une affectation d'un type primitif vers un type primitif et donc, il y a recopie de la valeur, non pas copie d'une réference.

Créons une deuxième classe

On conserve la classe Facture telle qu'elle en rajoutant seulement un champ attribut :
public Client clientFacture;


Si on tente la compilation, on obtient une erreur :

Facture.java:9: cannot resolve symbol
symbol  : class Client
location: class Facture


En effet, il n'existe pas de classe Client et le type est inconnu. Il faut rajouter la définition une classe Client.java. Cette définition peut se faire dans le même fichier ou dans un nouveau fichier.

Si on choisit de définir la classe Client dans le même fichier : comme la classe Facture est public, elle doit être déclarée dans un fichier Facture.java et le fichier ne doit pas contenir la définition d'autre classe public. Si on veut définir la classe Client dans le même fichier, elle ne doit pas être public, le fichier s'organise alors de la sorte :
public class Facture{
/*Définition des méthodes et attributs de la classe Facture, éventuellement d'une fonction main*/
}

class Client{
/*Définition des méthodes et attributs de la classe Client*/
}


Dans ce cas, la classe client n'est pas visible des autres classes, elle ne sera pas utilisable par les autres classes. Pour que la classe Client soit utilisable, visible, on l'enregistre dans un nouveau fichier : Client.java, dont la définition complète est donnée ci-dessous :
public class Client{
	
	int numClient;
	
	int nbFacturesClient=0;


public Client(int num)
	{
	numClient=num;
	}

public void ajouterUneFacture()
	{
	nbFacturesClient=nbFacturesClient+1;
	}
}


En enregistrant une telle classe à côté de la classe Facture et en appelant la compilation de Client (on verra plus tard le cas où les classes ne sont pas dans le bin), on voit que Client.java est compilé en Client.class automatiquement. Désormais l'un des attribut de client est un objet et non plus un type primitif, on peut faire des actions sur cet objet :

/*On crée un objet Facture*/
Facture fact1=new Facture(4,1.6);
/*On crée un objet client*/
Client clt=new Client(5);

/*On affecte le client à la facture*/
fact1.clientFacture=clt;
/*il est alors possible d'utiliser les attributs et les méthodes du client de la facture : */
/*on change le numéro du client*/
fact1.clientFacture.num=6;
/*On ajoute une facture sur le client*/
fact1.clientFacture.ajouterUneFacture();


La syntaxe peut bien sur se généraliser : obj1.attr1.attr67.att3.methode(); ou obj1.attr1.attr5.x; qui fait réference à l'attribut x de l'objet attr5 de l'objet attr1 de l'objet obj1.

Initialisation par défaut et NULL

Le constructeur provoque une initialisation par défaut des différentes variables avant l'exécution des instructions du constructeur. Les attributs numériques sont initialisées à 0. Les attributs objets sont initialisés à null, qui est la valeur d'une variable objet qui ne pointe sur rien. Les variables booléennes sont à faux. Les variables de type char sont initialisés à ' '. Ce sont ensuite les valeurs affectées dans la ligne de définition des attributs qui est prise en compte avant l'exécution des instructions du constructeur. On a en effet vu dans la définition d'une classe qu'il est possible de définir des attributs en faisant une affectation en même temps :



/*Le cas de la déclaration des attributs de classe avec affectation*/
public int nbUniteesvendues=5;
public Client clientFacture=new Client(3);

/*Le cas de la déclaration des attributs de classe sans affectation*/
public int nbUniteesvendues;
public Client clientFacture;




Attributs et méthodes static

Certains attributs et méthodes sont utilisables hors de l'instanciation : on parle d'attributs et de méthode static. La définition de tels attributs se fait en mettant static entre le niveau de visibilité et le type de l'attribut pour les attributs et entre le niveau de visbilité et le type de retour pour les méthodes. Par exemple, on peut rajouter un attribut nbFactures qui compte le nombre d'objet Facture instanciés, on modifie alors le constructeur :

public static int nbFactures=0;

public Facture(int nbUnites,double prix)
	{
	nbUniteesVendues=nbUnites;
	prixUnitaire=prix;
	montant=0;
	nbFactures++;
	}


L'initialisation des attributs static se fait une unique fois au chargement de la classe. Attention : il n'est pas possible d'utiliser un attribut non static depuis une méthode static, il n'est pas possible d'utiliser une méthode non static depuis une méthode static. Un exercice illustre. Il est possible d'appeller l'attribut par son nom seul dans la classe, mais comme on n'a pas d'objet pour le reférencer à l'extérieur fact1. ?, l'appel pour modification ou récupération de la valeur se fait par Facture.nbFactures, on parle d'attributs de classe. De même, l'appel aux méthodes static se fait par Facture.methode1() et on parle de méthode de classe, il n'y a pas d'instanciation prélable à l'utilisation de ces méthodes et de ces attributs.

Visibilités, accesseurs et modifieurs.

Que se soit pour les attributs ou pour les méthodes, on peut définir un niveau de visibilité. En Java : trois niveaux de visbilité sont possibles : Public, Private et Protected. Quelque soit le niveau de visibilité d'un attribut ou d'une méthode, il ou elle est toujours utilisable depuis toutes les méthodes de la classe à laquelle il appartient. Quand un attribut est Private, alors il n'est utilisable que depuis les méthodes de cette classe. Quand un attribut est Public, il est utilisable depuis n'importe quelle méthode de n'importe quelle classe. Notamment, les attributs sont modifiables depuis n'importe quelle classe. Enfin, pour les méthodes et les attributs Protected, ils sont utilisables seulement dans les classes dérivées de la présente classe (voir le chapitre sur l'héritage).

Déterminer la visibilité d'un attribut n'est pas une chose évidente : on voudrait d'abord tout mettre à public pour n'avoir aucun problème d'accès et ne produire aucune erreur à la compilation. Supposons un cas concret. On définit un second attribut static pour la classe Facture :

static double montantTotalFacture=0;


Cet attribut vise à enregistrer le montant total facturé. Supposons que le prix de la facture soit augmenté de 1.5 depuis une autre classe qui utilise la classe Facture :

fact1.prixUnitaire=fact1.prixUnitaire+1.5;


Ceci peut être codé par un programmeur qui utilise la classe Facture codée par un autre ou par le programmeur de la classe qui, imprudent, oublie qu'il faut modifier l'attribut montantTotalFacture si on modifie le prix de la facture !!! Avec des attributs public, on peut très rapidement arriver à des incohérences. Une bonne démarche de programmation consiste à être prudent et à faire passer l'accès aux attributs d'une classe par des accesseurs pour en récupérer la valeur, par des modificateurs pour en modifier la valeur. Réglons alors notre problème :

private double prixUnitaire=0;

public double getPrixUnitaire()
	{
	return prixUnitaire:
	}

public void setPrixUnitaire(double nouveauPrix)
	{
	Facture.montantTotalFacture-=prixUnitaire*nbUniteesVendues;
	montant=nouveauxPrix*nbUniteesVendues;
	Facture.montantTotalFacture+=nouveauPrix*nbUniteesVendues;
	prixUnitaire=nouveauPrix;
	}



On maintient constamment la cohérence de la classe en faisant passer toute modification du prix par setPrixUnitaire(double). Pour la manipulation du prixUnitaire depuis une classe extérieure, on est obliger de passer par accesseur et modificateur, de sorte que la cohérence est garantie par rapport aux usages extérieures. Une bonne démarche serait de se contraindre soi-même à l'utilisation de ces méthodes pour manipuler prixUnitaire afin de ne pas produire de modifications non cohérentes à l'occasion d'une maladresse.

Noter que si on ne met pas de niveau de visibilité, cela vaut une déclaration à private par défaut.

2 Des élements plus généraux sur l'organisation des classes, la compilation etc...

Constructeur par défaut et pré-initialisation des variables

Dans une classe où on ne définit pas un constructeur de manière explicite, il existe un constructeur implicite.
public class ClasseConstructeur
{

public void affiche5()
	{
	System.out.println(5);
	}	
}


Dans ce cas, le constructeur implicité est ClassConstructeur() et donc, pour obtenir un objet : ClasseConstruceteur c=new ClasseConstructeur(). Dans le cas où au moins un constructeur est défini, le constructeur par défaut n'est plus dipsonible :
public class ClasseSansConstructeur
{
	int val=0;

public ClassCOnstructeur(int n)
	{
	val=n;
	}

public void afficheN()
	{
	System.out.println(val);
	}	
}
Si on veut disposer du constructeur sans argument, il faut alors le sépcifier explicitement :
public class ClasseSansConstruceur
{
	int val=0;

public ClassCOnstructeur(int n)
	{
	val=n;
	}

public ClassConstructeur()
	{
	val=5;
	}

public void afficheN()
	{
	System.out.println(val);
	}	
}


Lors de l'exécution d'une méthode constructeur, avant l'exécution des lignes du programmeur, il y a initialisation implicite des variables : les int, double, float etc... sont initialisés à 0, les variables de type objet sont de valeur null. Si dans le constructeur, une variable n'est pas affectée, alors elle resterait à cette valeur. Notamment, dans le cas d'un constructeur implicite, les initialisations es objets sont les initialisations par défaut.

Comparaison d'objet

Les opérateurs == et != s'appliquent théoriquement à des objets. Mais comme ils portent sur les réferences elles même, leur intérêt est très limité. Ainsi donc :
Facture a,b;
...
boolean bo=(a==b)
bo est à vrai seulement si a et b font réference à un seul et même objet, et non pas seulement si les valeurs des champs sont les mêmes.

Le ramasse-miette

Nous avons vu comment un programme peut donner naissance à un objet en recourantà l'opérateur new. A sa rencontre, Java alloue un emplacement mémoire pour l'objet et l'initialise (implicitement, explicitement par le constructeur). En revanche, il n'existe aucun opérateur pour détruire un objet et libérer l'espace mémoire. En fait, la démarche employée par java est un mécanisme de gestion automatique de la mémoire dénomé : ramasse miette qui traduit Garbage collector. Son principe : Soit par exemple l'instruction :
(new Facture(5,7.6)).calculMontant();
Dans ce cas, on crée un objet sans réference qui sera éliminé lors de l'activation du ramasse-miette. Soit un autre exemple :
Facture fac1=new Facture(5,78);
fac1=new Facture(6,7.8);
Ici on a crée deux factures, mais on a perdu la référence sur la première : le ramasse miette pourra l'éliminer.

Surdéfinition de méthodes

On parle de surdéfinition lorsqu'un même symbole peut avoir plusieurs significations différentes. En java, il peut y avoir surdéfinition des méthodes d'une classe. C'est à dire qu'avec un même nom de méthode, plusieurs méthodes existent. Soient deux classes (noter la possibilité qui serait généralement exploitée de créer une classe qui contient le main), noter que les types primitifs entiers sont, dans l'ordre de grandeur : byte, short, int, long :
public class Point
{

	private int x,y;

public Point(int abs,int ord)//constructeur
	{
	x=abs; y=ord;
	}

public void deplace(int dx,int dy)//deplace(int,int)
	{
	x+=dx; y+=dy;
	}

public void deplace(int dx)//deplace(int)
	{
	x+=dx;
	}

public void deplace(short dx)//dplace(short)
	{
	x+=dx;
	}
}


public class SurDef1
{

public static void main(String args[])
	{
	Point a=new Point(1,2);//appelle deplace(int,int)
	a.deplace(1,3);//appelle deplace(int)
	a.deplace(2);
	short p=3;
	a.deplace(p);//appelle deplace(short)
	}
}
En cas d'ambigüité, ie supposons la définition de :
public void deplace(int dx,byte dy)//deplace(int,byte)
	{
	x+=dx; y+=dy;
	}

public void deplace(byte dx,int dy)//deplace(byte,int)
	{
	x+=dx; y+=dy;
	}
Que se passe t'il alors avec le code suivant ?
int byte b;
a.deplace(b,b);
Ici l'appel est ambigü et provoquera une erreur de compilation.

Noter que notamment le constructeur peut être surdéfini.

Auto-réference, priorité des opérateurs et mot-clé this

Soit le code suivant :
public class Test
{
	private int x,y;
	private int n;
	private static int m;

public void incrementN(int x)
	{
	n+=x;
	}
}
Ce code ne provoque pas d'erreur ! Mais le x dans la fonction désigne t'il l'attribut de l'objet ou bien le paramètre passé à la fonction ? Il y a priorité, ici, le x est celui qui est passé en paramètre. Pour accéder la variable x de l'objet : on aurait mis this.x pour désigner le x de l'objet, Test.m pour être assuré qu'il n'y ait pas d'ambigüité. On peut tester le code suivant :
public class Test
{
	private int x=7;
	private static int m=5;

public void attribut(int x)
	{
	System.out.println(x);
	System.out.println(this.x);
	}

public void attributClass(int m)
	{
	System.out.println(m);
	System.out.println(Test.m);
	}

public static void main(String[] args)
	{
	Test t=new Test();
	t.attributClass(9);
	t.attribut(1);	
	}
}


Une autre utilisation de this : pour renvoyer une réference sur l'objet lui même : return this;

Une autre utilisation de this : appel d'un constructeur au sein d'un autre constructeur.
public class Point{

public Point(int abs,int ord)
	{
	x=abs;
	y=ord;
	}

public Point()
	{
	this(0,0);
	}
}
Attention : this doit être la première instruction du constructeur si on veut appeller un autre constructeur.

Classes internes

Il est possible de définir des classes internes à une classe. Voici le schéma de déclaration :
class E
{
...
	class I
	{
	...
	...
	}
...
}


Forme la plus générale d'une classe

package chemin du package;

import chemin du package;
import chemin du package;
import chemin du package;
...
import chemin du package;
import chemin du package;
import chemin du package;

[public|private] class nomClasse
{
}
La notion de package renvoie à un ensemble de classe regroupées sous un nom : le nom du package. La notion de package est proche de la notion de bibliothèque dans les autres langages. Deux types de package : ceux qui sont pré-définis dans l'API, ceux qui sont construits par le programmeur qui sépare son programme en package (par exemple une séparation entre classes de fonctionnement et classe d'affichage pour un même logiciel).

La première ligne package rattache la classe à un package : package .... Les lignes import chemin_de_package charge en mémoire vive les classes du package spécifié derrière le import, ce package pouvant être un package de l'API ou un package crée par le programmeur. Le programmeur peut avoir intérêt à séparer ses classes en package au bout d'un certains nombres de classes. Chaque classe "voit" toutes les classes de son package.

Il est possible d'avoir un même nom de classe dans deux packages différents, soit par exemple une classe Point commune à deux package P1 et P2, il est alors possible de distinguer les classes :
package MonPackage;

public class TestPackage{

}
Si on compile cette classe, elle ne sera pas utilisable tant que non pas placée dans un repertoire MonPackage. Deux solutions :

Conventions en java

Il existe des conventions dans la manière d'écrire du code en java. un nom d'attribut ou de méthode commence par une minuscule. Les autres caractèrs sont en minuscules sauf ceux qui débutent un nouveau mot : factureNumero1 par exemple. Le nom des classes fonctionne de la même manière, mais commence par une majuscule : class FactureClient.



Passage d'objet en paramètre d'une méthode / fonction

Lorsque l'on passe un objet en paramètre d'une méthode, le passage se fait pas référence alors que dans le cas des variables des types primitifs, les paramètres sont passés par valeur. Soit une fonction d'une classe GestionClient accorderUneRemise(Client clt,double taux); et soient les instructions :

public class GestionClient{

public static void main(String[] args)
	{
	GestionClient objetGestionClient=new GestionClient();
	
	double taux=0.075;
	Client clt=new Client("c:\\Clients\\client57.txt");
	
	objGestionClient.accorderUneRemise(clt,taux);
	System.out.println("La remise accordee pour le client "+clt.remiseAccordee);
	System.out.println("le taux "+taux);
	}

public void accorderUneRemise(Client clt,double taux)
	{
	clt.remiseAccordee=taux;
	taux=0;
	}

}

Ici l'affichage est :
La remise accordee pour le client 0.075
le taux 0.075


3 Le cas de String : non pas un type primitif mais une classe avec des abus de syntaxes



Premiers élements sur la classe String

On considère une première classe de l'API, celle-ci est chargée systématiquement : il n'est pas besoin de faire un appel explicite à son package.
String ch1,ch2;
ch1="Bonjour";
ch2="Bonsoir";
La notation ch1="Bonjour" vaut création d'un nouvel objet de type String qui est réferencée ensuite par ch1. Ce type de notation qui diffère des notations habituelles vues plus haut est assez rare en Java. Pour l'affichage des flux en sortie, on utilise les fonctions de la variable de classe out de la classe System : println(String) et print String. C'est ce que l'on faisait de manière implicite et sans le savoir plus haut en écrivant :
 
int u=25;
System.out.println("Valeur de la variable u : "+u);
La classe String contient une méthode length() qui permet d'obtenir la longueur d'une chaîne.

La classe String propose une méthode char charAt(int i) qui renvoie le ième caractère le chaîne.

La classe String propose une méthode char[] toCharArray() qui renvoie un tableau de char qui contient l'ensemble des caractères formant la chaîne.

L'opérateur + sert la concaténation de chaînes.
String ch1="Le langage";
String ch2="Java";
String ch=ch1+ch2;
La concaténation de deux String vaut création d'un nouvelle objet de type String.

L'opérateur ne pose pas de problème dans le cas où il s'applique sur des String. Il est possible de mélanger chaînes et expressions d'un type primitif. C'était le cas dans les utilisations précédentes.
 
int u=25;
System.out.println("Valeur de la variable u : "+u);
Dans ce cas, la variable u donne lieu à la création d'une String avant concaténation.

Le cas des objets. Que se passe t'il donc lorsque c'est un objet que l'on met dans une situation de ce type :
Facture fact1=new Facture(5,6.3);
System.out.println("La facture fact1 donne en affichage : "+fact1+" quelle interprétation peut-on en donner ?");
Pour des raisons qui seront explicitées aux chapitres 4 et 5, si l'utilisateur a défini une méthode String toString() dans sa classe, alors c'est la chaîne renvoyée par cette fonction qui sera concaténée pour l'affichage, sinon c'est une chaîne qui donne l'adresse en mémoire vive qui est renvoyée.

Comparaison de chaînes

Pour les raisons évoquées plus haut, et comme String est une classe et non pas un type primitif, on ne peut pas comparer deux String avec == et !=, une telle comparaison comparerait seulement les réferences. Cependant, la classe String propose une méthode de comparaison : boolean equals(String str) :
String str="Bonjour";
String str2="Bonsoir";
String str3="Bonsoir";
System.out.println(str.equals(str2));//Affiche false
System.out.println(str2.equals(str3));//Affiche true
System.out.println(str3.equals(str2));//Affiche true
System.out.println(str3.equals("bonsoir"));//Affiche false
Voici évoquée une première classe de l'API : nous n'avons pas donné l'ensemble des méthodes ou des attributs de la classe String, de la même manière que nous ne donnerons pas l'ensemble des méthodes et des attributs de toutes les classes de l'API, en revanche, on peut faire réference au site de sun qui reproduit via ses pages l'ensemble de cette hiérarchie, et notamment pour la classe String :
Classe String sur le site de sun

4 Un point sur les variables et les méthodes

Dans une méthode non static, il y a différents types de variables :

Que se passe t-il si une variable locale a même nom qu'un attribut de la classe ? ie :
public class A{

public static void main(String[] args)
	{
	A varA=new A();
	varA.att1=6;
	varA.methode1(5.0,6);
	}

public int att1;

public void methode1(double att1,int a1)
	{
	System.out.println(att1);
	}
}
Dans ce cas, c'est la variable locale qui est utilisée par le programme qui affiche "5.0", pour manipuler l'attribut de l'objet : this.att1. Si att1 était un attribut de classe, on utilisera A.att1 pour manipuler l'attribut de classe.